dan@ttds.UUCP (Dan Sahlin) (01/13/84)
<blank line> Someone claimed that NULL was exactly the same as 0. This is however not true of you have a compiler with 16 bit integers, and 32 bit addresses. I have spent a lot of time "debugging" programs that implicitly assume that pointers and integers have the same length. Dan Sahlin (..mcvax!enea!ttds!dan)
guy@rlgvax.UUCP (Guy Harris) (01/14/84)
Actually, in <stdio.h> NULL is defined as 0, which is really as it should be. So whether you use 0 or NULL makes no difference (unless somebody made the mistake of defining NULL as "(char *)0"); you still have to cast it explicitly. Then again, that's what "lint" is for; it's a good idea to run "lint" on any program which isn't a quick throw-away or very small and written by somebody you *know* won't make type errors - on the other hand, I don't know anybody like that, myself included. (If NULL were defined as "(char *)0", "lint" would get gastric distress whenever NULL were passed as a pointer argument to a routine which expected an "int *" or something like that. Furthermore, if you tried to assign "(char *)0" to an "int *" variable, PCC would justifiably complain about an illegal pointer combination. C doesn't have the notion of a generic "null pointer", it has a "null pointer to char" which is distinct from "null pointer to int" which is distinct from "null pointer to (struct proc)"....) Guy Harris {seismo,ihnp4,allegra}!rlgvax!guy
ron%brl-vgr@sri-unix.UUCP (01/15/84)
From: Ron Natalie <ron@brl-vgr> I'm sorry but whoever claimed NULL was the same as zero was correct. Zero is the only integer you are assured to be able to assign to or compare with a pointer. If you can't it is a bug with your C implementation. -Ron
gwyn%brl-vld@sri-unix.UUCP (01/16/84)
From: Doug Gwyn (VLD/VMB) <gwyn@brl-vld> However, zero pointers come in several different sizes in some implementations of C, with (char *) guaranteed to be the widest. This means that passing an actual argument of 0 to a function expecting a pointer may not necessarily work, unless the zero is explicitly type-cast to the corresponding pointer type. E.g., func( (struct foo *)0, more_args ); or func( (struct foo *)NULL, more_args ); rather than func( 0, more_args ); or func( NULL, more_args ); if the first formal parameter of foo() is declared a (struct foo *).
kvm@basservax.SUN (Karlos Mauvtaque) (01/18/84)
Dennis Ritchie says that 0 is the 'null pointer'. Read your reference manual guys. There is no place in the language where there is null pointer ambiguity. 'null pointer's as arguments to function calls must be cast to the correct pointer type whether you use NULL or 0. You can't assume that (char *) is the same size as all other pointers so #define NULL ((char *)0) won't do you any good. I use NULL rather than 0 because it's good style.
mark@cbosgd.UUCP (01/24/84)
If NULL and 0 are supposed to be the same, please explain what proper behavior is in the following situation: execl("/bin/echo", "echo", "hi", "there", 0); (This is the way it's documented in exec(2) and used everywhere.) Now, suppose you have a system where ints are 16 bits and char *'s are 32 bits. (Hmm, sounds like a 68000.) Now, it's clear we have a series of 32 bit values on the stack, followed by a 16 bit zero, followed (if you keep looking) by garbage. The only clean solution I know of (other than using 32 bit ints, which is inefficient) is to use NULL and have NULL be (void *) 0. This is obviously pretty painful, given all those programs out there that use exec. Considering that it's quite reasonable for either the high 16 bits or the low 16 bits of a valid 32 bit pointer to be zero, is there some other reasonable way to handle this?
gwyn%brl-vld@sri-unix.UUCP (01/26/84)
From: Doug Gwyn (VLD/VMB) <gwyn@brl-vld> The final argument to execl(2) should not be documented as 0, that's left over from the days when it didn't matter. The only sane thing for the terminator to be is a (char *)0, for uniformity with the argument list.
ron%brl-vgr@sri-unix.UUCP (01/26/84)
From: Ron Natalie <ron@brl-vgr> This is wrong if you want to be strict about things: execl("/bin/echo", "echo", "hi", "there", 0); And so is this! execl("/bin/echo", "echo", "hi", "there", NULL); in system V (and most everything else) as NULL really is 0. You are only guaranteed of being allowed to mix 0 and pointers in assignment (that's with an equals, not passing to functions) and comparison. The correct way is: execl("/bin/echo", "echo", "hi", "there", (char *)0); Because execl's arguments must be character pointers not ints! -Ron
john@genrad.UUCP (John Nelson) (02/01/84)
After hearing all the controversy that NULL is identically equal to zero, the only sane solution is to fix the C language. Add a special constant that is the equivalent of zero, but is a pointer. Since NULL already has bad connotations, let's borrow from PASCAL, and call it NIL. Now NIL is guaranteed to be a pointer of the largest type (char * ?) but it will compare against any pointer without error and it can be passed as a subroutine argument. NIL can be assigned to any pointer type, and will always be guaranteed to be different than a valid pointer. It would be nice if the definition of NIL included the restriction that a dereference of NIL would always cause a run-time error, but maybe this is too much to ask for. Those of us without the "fixed" compiler can #define NIL ((char *) 0) and use it anyway (although some of the compilers may complain about "illegal pointer combination"s)
guy@rlgvax.UUCP (Guy Harris) (02/03/84)
> Since I started this NULL vs. 0 discussion, I perhaps should repeat > my reasons for wanting people use to NULL instead of 0. > + 0 is 16 bits on my machine. > + NULL is declared as (char *)0 on my machine which makes 32 bits. > + Programs that use 0 as pointer *crash*. > + Programs that use NULL *don't crash*. > The reasons (although very obvious) have been very well elaborated > earlier so I will not repeat them. What fixes the problems caused by your first point (as elaborated by stating that pointers are 32 bits on your machine), your third point, and your fourth point is not using 0 vs. NULL, it's using 0 vs (whatever *)0. NULL solves this problem *only* if it is declared as (char *)0. It is *NOT* so declared on our (16-bit int, 32-bit pointer) machine, and it will not be. The correct way to fix the problem is to run your programs through "lint" and fix the type disagreements between formal and actual arguments. After all, is the point of this exercise to produce programs which don't break in the immediate environment, or is it to produce programs that are as type-correct as possible? Doing the latter brings benefits in terms of code correctness that the former doesn't bring (I can attest to bugs that have been uncovered as a result of running a program through "lint" - or even noticing the warnings that PCC gives you!). > While hoping to end this NULL discussion soon, I still have a question: > Is there really a C-compiler that makes a difference between a "(char *)0" > and "(int *)0" so a program can *crash* if it uses the wrong one? I'd be willing to bet that C implementations on Data General machines and on some Honeywell Level 6 minis make that distinction. There is *NO* guarantee whatsoever that there will not be such implementations of C; the language permits them, so if you want to write portable code you *must* assume that (char *)0 and (int *)0 may be completely incompatible beasts. Just declaring NULL to be (char *)0 and using it universally is sloppy coding practice. Period. It may patch over the problem on machines with 16-bit ints and 32-bit pointers, but the correct solution (casting 0 or NULL to the proper value) doesn't cost more than minor programmer inconvenience and is far more likely to work in general. Guy Harris {seismo,ihnp4,allegra}!rlgvax!guy
guy@rlgvax.UUCP (Guy Harris) (02/04/84)
> After hearing all the controversy that NULL is identically equal to zero, > the only sane solution is to fix the C language. Add a special constant > that is the equivalent of zero, but is a pointer. Since NULL already has > bad connotations, let's borrow from PASCAL, and call it NIL. > Now NIL is guaranteed to be a pointer of the largest type (char * ?) but > it will compare against any pointer without error and it can be passed > as a subroutine argument. NIL can be assigned to any pointer type, and > will always be guaranteed to be different than a valid pointer. > It would be nice if the definition of NIL included the restriction that > a dereference of NIL would always cause a run-time error, but maybe this > is too much to ask for. > Those of us without the "fixed" compiler can #define NIL ((char *) 0) > and use it anyway (although some of the compilers may complain about > "illegal pointer combination"s) Well, #define NIL ((char *)0) will do exactly what the NIL you mention does. EITHER ONE will cause compilers to complain - CORRECTLY - about illegal pointer combinations. There is no such thing as a "generic pointer" in C. Period. As such, there is no such thing as a generic null pointer in C. Period. It may be awkward on non-byte-addressed machines to implement (char *) and (int *) (or (anything else *)) so that (char *)&foo and (int *)&foo yield the same bit pattern. Furthermore, it is not necessary, as a correct C implementation will generate code to do the pointer format conversion if one assigns an (int *) to a (char *), or vice versa - with ONE exception: since there is no way to tell a C module that a function in another C module ("module" here means "source file") takes arguments of a certain type, no C compiler can generate code to do conversion of arguments to functions automatically. You have to help the compiler here by putting in explicit casts. I read a lot of articles on this subject whose real point seems to be that it's too much trouble to properly cast 0 or NULL when passed as an argument to a function. What's so hard about it? Those of us who've worked on machines with 16-bit "int"s and 32-bit pointers do it as a matter of habit. I even did it *before* I worked on such a machine, just because the minimal extra effort to write type-correct code (yes, you *can* write code which is 99% type-correct in C) is worth the effort. The only thing C "needs" is a way to declare the types of the arguments to a function, so that the compiler can generate the casts instead of requiring the user to do so. However, one can put in the proper casts explicitly, so C *isn't broken* in this regard. People may be too lazy to use C properly, but there is a rather low limit on the degree to which things should - or even can - be designed so that people who don't know how to use them won't break them. Please, people, if you don't yet know that there aren't generic pointers in C, and therefore that there is no such thing as a generic "null pointer" in C, and furthermore that there *shouldn't* be such a beast, please go back and read the C Reference Manual - especially the sections I referred to in an earlier article. It's been explained several times here why C doesn't need to be changed to "fix" the problem with NULL pointers; please don't force it to be explained again. Guy Harris {seismo,ihnp4,allegra}!rlgvax!guy
kre@mulga.SUN (Robert Elz) (02/05/84)
If defining a new "magic object" (NIL) would solve the problem at all, I'm sure that would have been done. However, the constant '0' already is that magic object, as far as the current syntax and semantics of C allow there to be one at all. The compiler cannot pass the "correct" type of null pointer to a function as an arg, unless (in the arg list) you tell it what the correct type is, as otherwise it has no knowledge of what that would be. It is no good at all to pass the "widest" null pointer, the only one that will do is the one that's just the right width. (Given a wider pointer, of known type, its always possible to convert it to another, narrower, pointer, but something has to do that conversion. Just dumping a wide pointer on the stack and hoping that its going to mean the right thing in a called function simply won't work). Should C be altered so that the compiler is able to determine what the right type of an arg is at a function call (ie: forward declararions) then '0' will serve the purpose of the "magic object" quite well enough, as it does in assignments to pointers, and comparisons. Robert Elz decvax!mulga!kre
dan@ttds.UUCP (Dan Sahlin) (02/09/84)
Since I started this NULL vs. 0 discussion, I perhaps should repeat my reasons for wanting people use to NULL instead of 0. + 0 is 16 bits on my machine. + NULL is declared as (char *)0 on my machine which makes 32 bits. + Programs that use 0 as pointer *crash*. + Programs that use NULL *don't crash*. The reasons (although very obvious) have been very well elaborated earlier so I will not repeat them. While hoping to end this NULL discussion soon, I still have a question: Is there really a C-compiler that makes a difference between a "(char *)0" and "(int *)0" so a program can *crash* if it uses the wrong one? Dan Sahlin ( ..mcvax!enea!ttds!dan )
jr@qtlon.UUCP (02/18/84)
The system V manual says that NULL is 0. -- ++jim <england>!ukc!qtlon!jr ++jim