Path: utzoo!mnetor!uunet!husc6!mailrus!ames!umd5!eneevax!mimsy!chris From: chris@mimsy.UUCP (Chris Torek) Newsgroups: comp.lang.c Subject: Re: Header problems Message-ID: <10574@mimsy.UUCP> Date: 9 Mar 88 02:00:26 GMT References: <2550049@hpisod2.HP.COM> <7412@brl-smoke.ARPA> <3351@chinet.UUCP> Organization: U of Maryland, Dept. of Computer Science, Coll. Pk., MD 20742 Lines: 89 >In article <7412@brl-smoke.ARPA> gwyn@brl.arpa (Doug Gwyn) writes: >>The implementor should put something like the following in each header: >> #define NULL 0 0) right In article <3351@chinet.UUCP> dag@chinet.UUCP (Daniel A. Glasser) writes: >Your use of NULL === 0 promotes unportable code, 1) wrong >On machines were sizeof int != sizeof(void *), the above definition will >not work on older style function calls (without prototypes) or in var-arg >situations. 2) poorly stated. Incorrect code, such as the following: main { f(NULL); exit(0); } /* wrong */ f(s) char *s; { if (s != NULL) printf("%s\n", s); } will indeed fail on such machines. Correct code, acheived by changing the first line to main { f((char *)NULL); exit(0); } /* right */ will work properly with `#define NULL 0'. Similar incorrect code will, on some machines, fail no matter what the definition of NULL. Leaving out the prototype (where available) or the cast is an error; making it appear to work is, I claim, a disservice. >A better way to do it, in each place you want to define NULL, is: > #ifndef NULL > #define NULL ((void *)0) > #endif The dpANS legislates that this must work, again with the proviso that the source code be correct (prototypes and/or casts present everywhere). The type (void *) does not exist in many pre-dpANS compilers, hence I claim this definition is not as good. All it does is hide the old Vax-era errors on some other machines. It does not fix them. >What the C language guarentees is not that 0===NULL, but that a constant 0 >can be assigned/compared to a pointer without warning or error regardless >of pointer representation and compare as equal to the NULL pointer. Correct (although I think it is better stated as `... compare as equal to a nil pointer of the type of the first pointer'). >In cases where the compiler is unable to determine if an int or type * >is being passed, 0 is passed as an int. The use of the name NULL in the >preprocessor does not affect this. Also correct. The solution, however, is to provide the context via casts and/or prototypes, not to use (void *), which (if it exists at all) may not have the same representation as, e.g., (int *) (and in fact does not on several existing machines). The following code is incorrect: #undef NULL #define NULL ((void *)0) main() { f(NULL); exit(0); } /* wrong */ f(p) int *p; { if (p != NULL) *p = 11; } (whether it appears to work on your machine is irrelevant). This version is correct: #undef NULL #define NULL 0 main() { f((int *)NULL); exit(0); } /* right */ f(p) int *p; { if (p != NULL) *p = 11; } Finally, note that in this last example, NO SINGLE DEFINITION OF NULL can make the two lines marked `wrong' correct: main() { f1(NULL); /* wrong */ f2(NULL); /* wrong */ exit(0); } f1(cp) char *cp; { if (cp != NULL) *cp = 'a'; } f2(dp) double *dp; { if (dp != NULL) *dp = 2.2; } The only way to fix this last example is by adding prototypes and/or casts. -- In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7163) Domain: chris@mimsy.umd.edu Path: uunet!mimsy!chris