Path: utzoo!mnetor!uunet!husc6!bu-cs!polygen!pablo From: pablo@polygen.uucp (Pablo Halpern) Newsgroups: comp.lang.c Subject: Re: Why NULL is 0 - summary Message-ID: <132@polygen.UUCP> Date: 19 Mar 88 23:43:27 GMT References: <800@zippy.eecs.umich.edu> Organization: Polygen Corporation, Waltham, MA Lines: 111 A while ago I wrote a couple of postings that suggested solutions to the dilema of how to define NULL. I got a number of responses, both by EMAIL and posted on the net which lead to me retract my position. As this discussion has been going on for a while, I thought I would summarize what I have learned (from Chris Torek and others) in the hopes that we might soon put and end to it. I'll start by restating the problem as I understand it. I will use a semi-formal notation. Given the following declarations: typedef foo sometype; sometype *p; extern void func(); /* note: no argument prototype */ In a separate file: void func(arg) sometype *arg; /* same definition of sometype as above */ { ... } The problem is to find a definition for NULL which will allow the following statements and expressions to be syntactically and semantically correct: 1) p = NULL; 2) p == NULL 3) func(NULL); Some suggested solutions to this problem were: a) #define NULL 0 b) #define NULL 0L c) #define NULL ((void *) 0) d) (paraphrasing my own suggestion) #if LONG_POINTER #define NULL 0L /* if sizeof(sometype *) == sizeof(long) */ #else #define NULL 0 /* if sizeof(sometype *) == sizeof(int) */ #endif All four solutions work equally well for expressions 1) and 2). The real difficulty is in getting expression 3) to work. Unfortunately none of the above solutions will work in *all* environments and there are some not-so-rare environments where *none* of the above solutions would work. The reason for this is that they all make one or more of the following assumptions about the machine and/or compiler which are not guaranteed by either K&R or ANSI-XJ311: a), b), and d) assume that the bit representation of integer zero is the same as the bit representation of nil for every pointer type. a), b), and d) assume that a pointer is the same width as some integral type. a), b), and d) assume that all pointer types are the same width. This assumption is false on PR1MEs (old compiler) and 8086 middle memory model code where function pointers are different than data pointers. The previous assumption does not apply to c), but c) does assume that all pointers are all coerced to the same width and representation when passed as function arguments. In other words, these solutions fail because, in the absence of a prototype, the compiler does not know the width or representation of the formal parameter and cannot coerce the argument appropriately. It has been suggested, by myself and others, that pointers should be coerced to a "universal pointer" representation when passed as a function argument in the absence of a prototype, in much the same way that shorts are coerced to ints. This would allow solution c) to work but could add a great deal of conversion overhead, as the forced float to double coersion has already shown. c) also has the attribute that a compiler that supports (void *) is also likely to support prototypes. This leads me to the only two solutions that would always work: e) ALWAYS use function argument prototypes. f) ALWAYS cast NULL when passing it as a function argument in the absence of a prototype. These solutions work with any reasonable definition of NULL. Of course, if your compiler does not support argument prototypes, then f) is the only portable solution. e) and f) can, of course, be used together. I and many of my fellow programmers have been spoiled by machines like the 680x0, where all of the above assumptions are true. Even when we worked on an 8086, we used the large memory model, where all pointers are the same width. In the code I looked at, NULL was virtually never used with a cast. Our code is supposed to be portable but most of our compilers don't support prototypes. Therefore, I have adopted solution d) until we can get those casts in. I am not a sloppy coder (on the contrary, I consider myself very neat and disciplined), I just didn't quite see the light early enough on this one :-(. Time to stop ignoring those messages from lint! Someone once complained that ANSI should not have put prototypes into the standard because, while they don't break existing code, they don't codify existing practice either. With the type of code shown in expression 3) extremely common, prototypes are good because they can be used to actually repair formerly non-portable code. I'm glad they did it! By summarizing my thoughts here, I hope I made things better rather than worse. If you post a follow-up, please also e-mail a reply, since I may soon put this entire subject into my kill file. A big THANK YOU to all who helped me sort this out! Pablo Halpern | mit-eddie \ Polygen Corp. | princeton \ !polygen!pablo (UUCP) 200 Fifth Ave. | bu-cs / Waltham, MA 02254 | stellar /