[comp.lang.c] references to dereferenced null pointers

karl@haddock.ima.isc.com (Karl Heuer) (03/15/90)

In article <1990Mar14.164539.23685@utzoo.uucp> henry@utzoo.uucp (Henry Spencer) writes:
>There is absolutely nothing wrong with having a pointer representation in
>which the bit pattern for a null pointer is not all zeros... except that
>there are a lot of old, badly-written programs which will break.  Thus my
>earlier comment that it is valid but unwise.

Note that "p = 0", "p == 0", "!p", "char *f() { return 0; }" are *not*
examples of such badly-written code; they may be bad style, but the compiler
is required to generate correct code involving a true null pointer.  The only
"dangerous" context (other than hacking with unions and such) is when a null
pointer constant is being passed as an argument to a function.  (In C++ and
ANSI C, any argument not covered by a prototype.  In old C, any function
argument at all.)  In particular, neither of the two calls
	execl("/bin/sh", "sh", "-i", 0);
	execl("/bin/sh", "sh", "-i", NULL);
is correct; it should be written as either of
	execl("/bin/sh", "sh", "-i", (char *)0);
	execl("/bin/sh", "sh", "-i", (char *)NULL);

But this problem can occur even without strange null pointers: such sloppy
code will already break on certain implementations where pointers and ints
have different lengths.

Karl W. Z. Heuer (karl@ima.ima.isc.com or harvard!ima!karl), The Walking Lint
Followups to comp.lang.c.

williams@umaxc.weeg.uiowa.edu (Kent Williams) (03/15/90)

In article <16179@haddock.ima.isc.com> karl@haddock.ima.isc.com (Karl Heuer) writes:
>In article <1990Mar14.164539.23685@utzoo.uucp> henry@utzoo.uucp (Henry Spencer) writes:
>>There is absolutely nothing wrong with having a pointer representation in
>>which the bit pattern for a null pointer is not all zeros... except that
>>there are a lot of old, badly-written programs which will break.  Thus my
>>earlier comment that it is valid but unwise.
>
>Note that "p = 0", "p == 0", "!p", "char *f() { return 0; }" are *not*
>examples of such badly-written code; they may be bad style, but the compiler
>is required to generate correct code involving a true null pointer.

The bottom line in actual practice is that if NULL isn't a binary object 
of all zero bits, you can get into trouble porting programs.  Using 0 and
NULL interchangebly is an unfortunate but common practice -- see code
examples in Stroustroup's C++ book -- many assignments of 0 to
pointers.

In order to port a program to a not-all-zero-bits-NULL architecture
would be aided by a compiler that would flag ALL assignments of
non-pointer constants to pointer variables.  This wouldn't solve all
problems, since you can have a function like:

void assign_int_to_pointer(void **x, int y) { *x = (void *)y; }

Then your compiler would have to generate RUN-TIME checks of values
passed in.

To muddy the waters further, It isn't uncommon (and not terribly
unportable) to use a set of small integral constants, say 0 .. 10 as
sentinel values assigned to pointers, e.g.

		switch((int)ptr) {
		case 0:	do0(); break;
		case 1: do1(); break;
				.
				.
				.
		default:
				it_really_is_a_pointer(ptr);
		}

When discussing these issues, you do have to take into account actual
practice, whether actual practice is a good idea in the final analysis
or not.
--
                               
Kent Williams                  "We're One! All One! Exceptions Eternally?
williams@umaxc.weeg.uiowa.edu   None! Absolutely None!" - Dr. Bronner's Soap

john@hcr.uucp (John R. MacMillan) (03/16/90)

Karl Heuer <karl@haddock.ima.isc.com> writes:
|>There is absolutely nothing wrong with having a pointer representation in
|>which the bit pattern for a null pointer is not all zeros... except that
|>there are a lot of old, badly-written programs which will break.  Thus my
|>earlier comment that it is valid but unwise.
|
|Note that "p = 0", "p == 0", "!p", "char *f() { return 0; }" are *not*
|examples of such badly-written code; they may be bad style, but the compiler
|is required to generate correct code involving a true null pointer.  The only
|"dangerous" context (other than hacking with unions and such) is when a null
|pointer constant is being passed as an argument to a function.

``Zeroing out'' pointers with memset, bzero, or calloc is another
all-too-common problem.
-- 
John R. MacMillan	| "All the best freaks are here; please stop staring
HCR Corporation		|  at me."
{utzoo,utcsri}!hcr!john	| -- Marillion

jenkins@jpl-devvax.JPL.NASA.GOV (Steve Jenkins) (03/16/90)

Instructions for posting comments on the relationships among
0, NULL, and nil pointers:

    1.  Read K&R2.  Carefully.

    2.  Read Chris Torek's postings on the subject.  You won't have to
	go back very far -- this comes up about every two months.

    3.  Read Doug Gwyn's postings on the subject.

    4.  Still want to post?  Go to step 1.

    5.  Resume normal net reading.

-- 
Steve Jenkins N6UNI			jenkins@jpl-devvax.jpl.nasa.gov
Caltech/Jet Propulsion Laboratory	(818) 354-0162

karl@haddock.ima.isc.com (Karl Heuer) (03/16/90)

In article <945@ns-mx.uiowa.edu> williams@umaxc.weeg.uiowa.edu.UUCP (Kent Williams) writes:
>The bottom line in actual practice is that if NULL isn't a binary object
>of all zero bits, you can get into trouble porting programs.  Using 0 and
>NULL interchangebly is an unfortunate but common practice -- see code
>examples in Stroustroup's C++ book -- many assignments of 0 to pointers.

As I just finished saying, those are quite legal and do not cause a problem on
"strange-NULL" implementations.  (I presume we're talking about *constant* 0.)

>would be aided by a compiler that would flag ALL assignments of
>non-pointer constants to pointer variables.

I would hope that *any* modern compiler would flag a pointer-vs-int collision!

>[An explicit pointer-to-integer cast can still cause problems]
>[Also, some programs use small integer constants as sentinels]

I'll admit that these two are problem situtations, but I doubt they're all
that common.  And you don't have to have a "strange-NULL" architecture for
them to break.

Karl W. Z. Heuer (karl@ima.ima.isc.com or harvard!ima!karl), The Walking Lint

goudreau@larrybud.rtp.dg.com (Bob Goudreau) (03/16/90)

In article <945@ns-mx.uiowa.edu>, williams@umaxc.weeg.uiowa.edu (Kent
Williams) writes:
> In article <16179@haddock.ima.isc.com> karl@haddock.ima.isc.com (Karl
Heuer) writes:
> >In article <1990Mar14.164539.23685@utzoo.uucp> henry@utzoo.uucp
(Henry Spencer) writes:
> >>There is absolutely nothing wrong with having a pointer representation in
> >>which the bit pattern for a null pointer is not all zeros... except that
> >>there are a lot of old, badly-written programs which will break.  Thus my
> >>earlier comment that it is valid but unwise.
> >
> >Note that "p = 0", "p == 0", "!p", "char *f() { return 0; }" are *not*
> >examples of such badly-written code; they may be bad style, but the compiler
> >is required to generate correct code involving a true null pointer.
> 
> The bottom line in actual practice is that if NULL isn't a binary object 
> of all zero bits, you can get into trouble porting programs.  Using 0 and
> NULL interchangebly is an unfortunate but common practice -- see code
> examples in Stroustroup's C++ book -- many assignments of 0 to
> pointers.

Sigh...

Looks like it's time for Chris Torek to repost his explanation of NULL
and 0....
 
------------------------------------------------------------------------
Bob Goudreau				+1 919 248 6231
Data General Corporation
62 Alexander Drive			goudreau@dg-rtp.dg.com
Research Triangle Park, NC  27709	...!mcnc!rti!xyzzy!goudreau
USA

das@lanai.cs.ucla.edu (David Smallberg) (03/17/90)

In article <1990Mar14.164539.23685@utzoo.uucp> henry@utzoo.uucp (Henry Spencer) writes:
>There is absolutely nothing wrong with having a pointer representation in
>which the bit pattern for a null pointer is not all zeros... except that
>there are a lot of old, badly-written programs which will break.  Thus my
>earlier comment that it is valid but unwise.

I've seen "the constant 0" misinterpreted in this way:

	thing *array[100];

		/* clear array to NULLs */   <-- WRONG
	(void) memset(array,0,sizeof(array));
		or
	(void) memset(array,NULL,sizeof(array));

The offender believes that since he's using the constant 0, the compiler
will use the appropriate bit pattern for a NULL pointer, even if that's not
all-zero-bits.

-- David Smallberg, das@cs.ucla.edu, ...!{uunet,ucbvax,rutgers}!cs.ucla.edu!das

throopw@sheol.UUCP (Wayne Throop) (03/18/90)

> From: guy@auspex.auspex.com (Guy Harris)
>> I'm confused, is a non zero NULL pointer valid or not?
> The confusion stems from confusion over the meaning of "non-zero".

While what Guy says in explanation is certainly correct, I think
that the confusion is over the meaning of "NULL pointer", not
non-zero.  That is, Guy explained things thoroughly, but the basis
of the problem is the distinction between properties of a name
of a thing and properties of the thing itself.

In the phrase "NULL pointer" in C, there are TWO levels of naming
going on, either one of which could have the property "zeroness".

Or let me put it this way.

In C, the name of the nil pointer is called "NULL".

But that's only what the name is CALLED, you see.

The NAME of the nil pointer is "0".

The nil pointer itself can have any bit pattern it pleases. 



The above sequence of statements explains
   1) why NULL should only be used as a pointer (this is a convention).
      It shouldn't be used to mean ascii NUL
   2) why the only proper definition of the macro NULL is the string "0".
      (or, actually, any way to spell the constant zero in C)
      (though, granted, the advent of ANSI C means that the best
       definition of NULL is arguably the string "((void*)0)" and
       variants on this theme.  Nevertheless "0" remains proper.)
   3) in just what way C's nil pointer may have a non-zero bit pattern.

It omits explanations of why one should never (in K&R1 C) pass NULL
as an argument without a cast to a specific pointer type.

It also omits explanation of the fact that there isn't really one
nil pointer, or (necessarily) one nil pointer bit pattern.  (This
is due to the fact that all pointer values in C have specific
types.)

So, to conclude, "NULL", in C should never have a "non-zero" definition.

Any particular nil pointer value (eg, one named ((void*)0)) can have any
bit pattern the implementor of a C language system chooses. 

--
Wayne Throop <backbone>!mcnc!rti!sheol!throopw or sheol!throopw@rti.rti.org

karl@haddock.ima.isc.com (Karl Heuer) (03/19/90)

In article <1990Mar15.184903.3397@hcr.uucp> john@troch.UUCP (John R. MacMillan) writes:
>Karl Heuer <karl@haddock.ima.isc.com> writes:
>|The only "dangerous" context (other than hacking with unions and such) is
>|when a null pointer constant is being passed as an argument to a function.
>
>``Zeroing out'' pointers with memset, bzero, or calloc is another

Right.  I had a hunch I was overlooking something!

Karl W. Z. Heuer (karl@ima.ima.isc.com or harvard!ima!karl), The Walking Lint

boyd@necisa.ho.necisa.oz (Boyd Roberts) (03/20/90)

In article <945@ns-mx.uiowa.edu> williams@umaxc.weeg.uiowa.edu.UUCP (Kent Williams) writes:
>
>To muddy the waters further, It isn't uncommon (and not terribly
>unportable) to use a set of small integral constants, say 0 .. 10 as
>sentinel values assigned to pointers, e.g.
>
>		switch((int)ptr) {
>		case 0:	do0(); break;
>		case 1: do1(); break;
>				.
>				.
>				.
>		default:
>				it_really_is_a_pointer(ptr);
>		}
>

Have you _completely_ lost your marbles?  The above is one of the most
disgusting and unportable constructs I've ever seen.  How do you know
what integer representations a pointer may have, unless you know the
target machine's architecture?

This sort of language abuse is sure to lay traps that will be sprung
when least expected.  Such code would probably pass most acceptance
tests, but would fail catastrophically at some later time.  Do you
really find it acceptable to write code that has such potential
for failure?


Boyd Roberts			boyd@necisa.ho.necisa.oz.au

``When the going gets wierd, the weird turn pro...''