decot@hpisod2.HP.COM (Dave Decot) (03/03/88)
If I am reading from an old draft, or am misunderstanding something, please feel free to (gently!) correct me. The requirement that headers described by the ANSI-C draft shall not define any "stray" (implementation-specific or internal) types or constants, and the requirement that inclusion of only one header shall be required for the use of any ANSI-C library function, should be dropped. While trying to avoid "name space pollution" is admirable, these requirements require implementations to break thousands of existing applications, or to define all constants in all headers. There is no place that I can define existing non-ANSI defined "extensions", since constants for them cannot be placed in the appropriate header, and I am not allowed to require applications to include another. I don't know in which header to define "NULL", since it is required by facilities that also require other constants, and I am not allowed to add these other constants to <stddef.h>. Dave Decot hpda!decot
gwyn@brl-smoke.ARPA (Doug Gwyn ) (03/04/88)
In article <2550049@hpisod2.HP.COM> decot@hpisod2.HP.COM (Dave Decot) writes: >While trying to avoid "name space pollution" is admirable, >these requirements require implementations to break thousands of existing >applications, or to define all constants in all headers. I don't understand your argument. It is true that the incredibly sloppy UNIX headers need to be cleaned up for ANSI C conformance. Based on the amount of trouble I've had porting applications due to name space pollution, I think this is a Good Thing. Unlike IEEE 1003.1, X3J11 is not nearly so concerned about allowing vendors to rubber-stamp "standard conforming" on whatever crufty implementations they may have already produced. A conforming ANSI C implementation will be considerably "cleaner" than is usually the case in existing practice. This was specified deliberately in order to actually promote application program portability among future standard-conforming implementations, rather than pay this all-important goal lip service while not resolving the most important portability issues. The current draft specifies the headers in which standard library functions are declared and those in which standard "manifest constant" macros are defined. With two exceptions (NULL and size_t), each such symbol is found in precisely one header. (Earlier drafts had this messed up.) size_t is defined in each header for which there is a reference to it in the corresponding section of the standard. >I don't know in which header to define "NULL", since it is >required by facilities that also require other constants, >and I am not allowed to add these other constants to <stddef.h>. NULL is defined in <stdio.h> (for historical compatibility) and in <stddef.h> (where it really belongs). If you need to use the symbol, you should include one or both of these two headers. Of course NULL is only a "convenience" macro, because it is clear how to write any type of null pointer constant in the language proper. It was included in the propsoed standard because so much code uses it, not because it is technically necessary for portability. The implementor should put something like the following in each header: #define NULL 0 P.S. These are my view, not necessarily X3J11's.
dag@chinet.UUCP (Daniel A. Glasser) (03/08/88)
In article <7412@brl-smoke.ARPA> gwyn@brl.arpa (Doug Gwyn (VLD/VMB) <gwyn>) writes: >NULL is defined in <stdio.h> (for historical compatibility) and in ><stddef.h> (where it really belongs). If you need to use the symbol, >you should include one or both of these two headers. Of course NULL >is only a "convenience" macro, because it is clear how to write any >type of null pointer constant in the language proper. It was included >in the propsoed standard because so much code uses it, not because it >is technically necessary for portability. > >The implementor should put something like the following in each header: > #define NULL 0 > > >P.S. These are my view, not necessarily X3J11's. Your use of NULL === 0 promotes unportable code, and is considered to be bad programming style in a world where portability across multiple machine architectures (ie, PDP-11, VAX-11, M680x0, I80x86 and Z800x) is required for commercial reasons. 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. Requiring sizeof int == sizeof(void *) is not a viable solution. A better way to do it, in each place you want to define NULL, is: #ifndef NULL #define NULL ((void *)0) #endif On older compilers, replace 'void' with 'char'. This allows the use of a short NULL representation when the compiler is smart enough to use it but forces passing of NULL in argument lists as a pointer sized object. 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. 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. ('===', as used above, means exactly equivilant) -- Daniel A. Glasser ...!ihnp4!chinet!dag || ...!ihnp4!mwc!dag || ...!ihnp4!mwc!gorgon!dag One of those things that goes "BUMP!!! (ouch!)" in the night.
g-rh@cca.CCA.COM (Richard Harter) (03/09/88)
In article <3351@chinet.UUCP> dag@chinet.UUCP (Daniel A. Glasser) writes: >In article <7412@brl-smoke.ARPA> gwyn@brl.arpa (Doug Gwyn (VLD/VMB) <gwyn>) writes: > >Your use of NULL === 0 promotes unportable code, and is considered to be >bad programming style in a world where portability across multiple machine >architectures (ie, PDP-11, VAX-11, M680x0, I80x86 and Z800x) is required >for commercial reasons. > This comes up regularly. There is nothing wrong defining NULL as 0; it does not make for non-portability. The fault lies in passing NULL as an uncasted pointer across a calling sequence. Since lint will tell you about this, there is no good reason for this happening. In actual fact, I am wary of using (void *)0 for portability reasons -- I don't know (and don't care) if all implementations that I have to deal with handle it correctly. I do know that all implementations handle (proper_type *)0 correctly. [Actually I don't know that, but I've never seen it fail.] -- In the fields of Hell where the grass grows high Are the graves of dreams allowed to die. Richard Harter, SMDS Inc.
chris@mimsy.UUCP (Chris Torek) (03/09/88)
>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
shirono@grasp.cis.upenn.edu (Roberto Shironoshita) (03/09/88)
In article <3351@chinet.UUCP> dag@chinet.UUCP (Daniel A. Glasser) writes: >In article <7412@brl-smoke.ARPA> gwyn@brl.arpa (Doug Gwyn (VLD/VMB) <gwyn>) writes: >>The implementor should put something like the following in each header: >> #define NULL 0 > >Your use of NULL === 0 promotes unportable code, and is considered to be >bad programming style in a world where portability across multiple machine >architectures (ie, PDP-11, VAX-11, M680x0, I80x86 and Z800x) is required >for commercial reasons. K&R says, on page 97 (I think it is the 22nd printing of the 1978 edition), "C guarantees that no pointer that validly points at data will contain zero ..." Since from what Doug says NULL remains in the language because of its widespread use in K&R-conforming programs, then his #define is valid. >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. Requiring sizeof int == sizeof(void *) is not a viable >solution. A better way to do it, in each place you want to define NULL, >is: > > #ifndef NULL > #define NULL ((void *)0) > #endif First I don't see where Doug requires that sizeof int == sizeof(void *). Second, your #define might break a lot of good code, which is what X3J11 tries to avoid by leaving NULL (as defined in K&R) in the standard. >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. You seem to forget that what the preprocessor does is to replace every occurrence of NULL (except inside strings and character literals) with whatever it was #define'd. So, the lexical analyzer doesn't see the lexeme `NULL'; it sees the constant 0. Hence, to the programmer, if there is a `#define NULL 0' anywhere in his code, NULL === 0. Your point is valid, in that the above doesn't necessarily mean that NULL === (void *)0 >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. True. Which is why a portability-conscious programmer should use explicit type casts in his function calls. Roberto Shironoshita ------------------------------------------------------------------------- 1- The University doesn't know I exist. | Internet: 2- Of course I may be wrong. | shirono@grasp.cis.upenn.edu
flaps@dgp.toronto.edu (Alan J Rosenthal) (03/09/88)
Doug Gwyn <gwyn@brl.arpa> wrote about NULL, and said that the proper definition of it is ``#define NULL 0''. dag@chinet.UUCP (Daniel A. Glasser) wrote: >Your use of NULL === 0 promotes unportable code ... [moralizing deleted] > >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. Requiring sizeof int == sizeof(void *) is not a viable >solution... > > #define NULL ((void *)0) >On older compilers, replace 'void' with 'char'. This allows the use of >a short NULL representation when the compiler is smart enough to use it >but forces passing of NULL in argument lists as a pointer sized object. This doesn't require sizeof(int) == sizeof(void *). The assumption is that you always cast NULL before passing it. Casting is still required if NULL is defined as ((void *)0); you can't assume that all pointers have the same size or representation. Passing a pointer to void to a routine that expects a pointer to struct gosh is as big a mistake as passing an int to a routine that expects a pointer to struct gosh. By the way, if you call f(0) where f expects a char *, the requirement for it to work is not simply that sizeof(int) == sizeof(char *), but also that (int)0 and (char *)0 have the same bit representation. With respect to your phrase "a pointer sized object" - there is no such thing in C. There may happen to be such a thing in a particular implementation of C due to hardware considerations. (Obviously, it is usually advantageous to represent all pointers in the same way (but not always); therefore this is a common occurrence.) ajr -- If you had eternal life, would you be able to say all the integers?
jdc@naucse.UUCP (John Campbell) (03/09/88)
In article <25398@cca.CCA.COM>, g-rh@cca.CCA.COM (Richard Harter) writes: > > > This comes up regularly. There is nothing wrong defining NULL as > 0; it does not make for non-portability. The fault lies in passing NULL > as an uncasted pointer across a calling sequence. Since lint will tell > you about this, there is no good reason for this happening. I've enjoyed and appreciated Richard's contributions to the net these past weeks, but he should realize that not all 'C' code is written on unix. I am (gasp) developing under VMS. I don't have lint; VMS compiler options are suppose to be all I need :-(. A while back a program called check(1) was posted. I could not, easily, get this to work on VMS. I looked at a local copy of the pre-processor for gnu-c and decided I didn't want to try to get that working on VMS. I should probably abandon VMS :-) but I get paid to work on it (paid to create code that might be non-portable due to not having a lint). PS anyone know of VMS lints for free? PPS who's writing the next ANSI lint? PPPS Thanks, again, to Chris Torek for NULL sanity. -- John Campbell ...!arizona!naucse!jdc unix? Sure send me a dozen, all different colors.
g-rh@cca.CCA.COM (Richard Harter) (03/10/88)
In article <604@naucse.UUCP> jdc@naucse.UUCP (John Campbell) writes: >In article <25398@cca.CCA.COM>, g-rh@cca.CCA.COM (Richard Harter) writes: >I've enjoyed and appreciated Richard's contributions to the net these >past weeks, but he should realize that not all 'C' code is written on >unix. I am (gasp) developing under VMS. I don't have lint; VMS compiler >options are suppose to be all I need :-(. This man is clearly one of the most astute and wisest contributors to this group :-). He makes a very good point, which I forgot. We develop on UNIX and port to VMS -- so we get to do all our lint checking before we go into alien environemnts. The great merit of Lint (versus thorough compiler diagnostics) is that it cross checks function argument and return value types. -- In the fields of Hell where the grass grows high Are the graves of dreams allowed to die. Richard Harter, SMDS Inc.
gwyn@brl-smoke.ARPA (Doug Gwyn ) (03/10/88)
In article <3351@chinet.UUCP> dag@chinet.UUCP (Daniel A. Glasser) writes: >In article <7412@brl-smoke.ARPA> gwyn@brl.arpa (Doug Gwyn (VLD/VMB) <gwyn>) writes: >> #define NULL 0 >Your use of NULL === 0 promotes unportable code, and is considered to be >bad programming style in a world where portability across multiple machine >architectures (ie, PDP-11, VAX-11, M680x0, I80x86 and Z800x) is required >for commercial reasons. > #define NULL ((void *)0) My original advice was precisely correct. Yours perpetuates the myth that all pointers have the same representation. Since this is not so in general, there is no alternative for the portable C programmer but to explicitly cast 0 or NULL to the proper type when it is used as a function parameter. For ANSI C, your suggestion is as good as mine (but no better); in non-ANSI C, there is no (void *). ANSI C has the additional advantage that either form of NULL used as a function parameter will be automatically converted to the right type, if a non-variadic function prototype is in scope. It seems that we have to go through this discussion every six months.
pablo@polygen.uucp (Pablo Halpern) (03/10/88)
From article <3351@chinet.UUCP>, by dag@chinet.UUCP (Daniel A. Glasser): > A better way to do it, in each place you want to define NULL, > is: > > #ifndef NULL > #define NULL ((void *)0) > #endif > > On older compilers, replace 'void' with 'char'. This allows the use of > a short NULL representation when the compiler is smart enough to use it > but forces passing of NULL in argument lists as a pointer sized object. Do NOT replace 'void' with 'char' for old compilers unless you are prepared to cast every use of NULL to the pointer type you are assigning (or comparing, etc.). Most compilers will complain of a pointer type missmatch since (char *) doesn't match (int *) or (struct foo *) etc.. I agree that for ANSI compilers, the above is correct, since (void *) DOES match (int *) by definition. For compilers that don't support (void *), you must have a compiler-specific definition of NULL: #define NULL 0 /* If sizeof(int) == sizeof(char *) */ or #define NULL 0L /* if sizeof(long) == sizeof(char *) */ This avoids pointer type missmatches because all compilers let you compare any pointer agains integer zero. Since the above #defines are necessarily machine (and compiler) specific, I put these types of declarations in a file called "machine.h" which contains machine and compiler-dependent declarations and is different for each compiler I use. "machine.h" is #include'd in all of my source files. Pablo Halpern mit-eddie \ Polygen Corp. princeton \ polygen!pablo 200 Fifth Ave. bu-cs / Waltham, MA 02254 stellar /
pedersen@acf3.NYU.EDU (paul pedersen) (03/10/88)
In article <10574@mimsy.UUCP> chris@mimsy.UUCP (Chris Torek) writes: >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. I have settled on the following approach, abandoning NULL altogether: # define NIL(TYPE) ((TYPE *)0) and now refer to all null pointers as NIL(char), NIL(int), NIL(struct foo), etc. Similarly, I use the following macros for allocating/freeing: char *malloc(); char *free(); # define ALLOC(TYPE) ((TYPE *) malloc(sizeof(TYPE))) # define FREE(p) (free((char *) p)) This syntax is pre-ANSI, but I don't see why it shouldn't work even post-ANSI, provided the declarations are changed to proper prototypes, and (void *) is introduced as needed. I have come to feel that the presence of an explicit cast in the code is bletcherous, similar to the presence of an explicit manifest constant. (For people who need to allocate vectors, a CALLOC macro with args TYPE and n is also probably in order; I haven't had a use for this yet.)
dag@chinet.UUCP (Daniel A. Glasser) (03/10/88)
Okay, I've now been flamed and roasted via mail, news and even on the phone for my posting about NULL and 0. If it were not for noisy and slow connections between the system I do my posting from and where I actually am, I would have written that to be the safest, you must cast NULL whenever you use it in a call. It was my recollection, and I did not have my K&R handy to check it out when I posted my message, that the C specification in K&R stated that when pointers to different objects had different representations, all pointers were passed in the "universal" pointer representation, just as char is promoted to int, float to double, so would (foo *) be promoted to the equivilent of (void *), and then moved back into a (foo *) representation when used at the other end. Having delt with compilers that did this, I assumed that the language had this orthoginality. I'm sorry, I aplogise to everyone who's life I have ruined with my posting. I made my posting not out of a lust for power, but because in my everyday work, I get about 3 calls a week passed to me from the tech. support people where I work from customers who are porting VAX unix code to the Atari ST using our compiler where ints are 16 bits, pointers 32 bits, and they often INSIST that K&R allows them to use 0 or NULL without casts in function calls expecting pointers. They get upset when I tell them that that is not the case. If it makes you feel any better, I'll retract what I said. However, the #ifndef ... #endif or the #undef should be used, and the EXPLICIT casting of any use of NULL in a parameter list should be stressed. <flame on> Please note that of over 100 programs that I have gathered from the network source groups, not one of them that I have actually looked at has cast NULL to any kind of pointer when using it in calls. Not one! Not even the GNU stuff that I've looked at. In other words, though proper usage uses casts on NULL, common usage does not. <flame off> -- Daniel A. Glasser dag@chinet.UUCP One of those things that goes "BUMP!!! (ouch!)" in the night. ...!att-ih!chinet!dag | ...!ihnp4!mwc!dag | ...!ihnp4!mwc!gorgon!dag
msb@sq.uucp (Mark Brader) (03/10/88)
> By the way, if you call f(0) where f expects a char *, the requirement > for it to work is not simply that sizeof(int) == sizeof(char *), but > also that (int)0 and (char *)0 have the same bit representation. *And* that the implementation passes integer and pointer type values to functions in the same manner. Remember, arguments can be passed in registers and machines can have several types of registers. *Never* say f(0) or f(NULL). Mark Brader, SoftQuad Inc., Toronto, utzoo!sq!msb, msb@sq.com #define MSB(type) (~(((unsigned type)-1)>>1))
mls@whutt.UUCP (SIEMON) (03/11/88)
In article <3416@chinet.UUCP>, dag@chinet.UUCP (Daniel A. Glasser) writes: > > ... > In other words, though proper usage uses casts on NULL, common usage > does not. > > Daniel A. Glasser dag@chinet.UUCP Yes; you should realize that you were flamed so much because people were feeling deeply guilty about their own failure to practice what they preach. Here was a perfect scapegoat -- someone who could be read as not understanding what to do and could be lectured about it by those who REALLY intend to do it right, but somehow don't quite get around to it. (Please no flames from those who really do go to the effort to do it right -- you are a rare breed; for most of us, it is just too damned easy to "code" and too damned hard to think and to rewrite. Michael Siemon contracted to AT&T Bell Laboratories (these are my opinions, not theirs) ihnp4!mhuxu!mls
chris@mimsy.UUCP (Chris Torek) (03/11/88)
[This makes three.] In article <121@polygen.UUCP> pablo@polygen.uucp (Pablo Halpern) writes: >For compilers that don't support (void *), you must have >a compiler-specific definition of NULL: No. > #define NULL 0 /* If sizeof(int) == sizeof(char *) */ > or > #define NULL 0L /* if sizeof(long) == sizeof(char *) */ Either is technically legal. The former suffices, is not machine dependent, and is correct. >Since the above #defines are necessarily machine (and compiler) specific, The definition of NULL (as 0, or if you have a dpANS-conformant compiler, as (void *)0) is not machine dependent. What *is* machine dependent is whether uncasted 0 in unprototyped function calls appears to work. Using uncasted 0L appears to work (but is nonetheless wrong) on IBM PCs using large model compilers. -- In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7163) Domain: chris@mimsy.umd.edu Path: uunet!mimsy!chris
jas@llama.rtech.UUCP (Jim Shankland) (03/11/88)
In article <3351@chinet.UUCP> dag@chinet.UUCP (Daniel A. Glasser) writes: >Your use of NULL === 0 promotes unportable code, and is considered to be >bad programming style in a world where portability across multiple machine >architectures ... is required.... On machines were sizeof int != >sizeof(void *), the above definition will >not work.... A better way to do it, in each place you want to define NULL, >is: > > #ifndef NULL > #define NULL ((void *)0) > #endif Ack! Here it comes again! Warm up the ``kill'' file. Jim Shankland ..!ihnp4!cpsc6a!\ sun!rtech!jas ..!ucbvax!mtxinu!/