null pointers (was: negative addresses)

Guy Harris guy at gorodish.Sun.COM
Wed Jun 8 12:45:32 AEST 1988


(The preceding article's contents represent a misunderstanding of the C
language, rather than a valid statement of what an architecture should or
should not have; I've therefore redirected followups to "comp.lang.c", although
many readers there are probably tired of seeing these same
misunderstandings....)

> This presents a sequential search through a linear list more positively than
> 
> 	#include <stdio.h>	/*stupid place for NULL*/
> 	...
> 	Foobar *foobar;
> 	for (foobar = first; foobar != (Foobar*)NULL; foobar = foobar->next)
> 	{
> 	}
> 
> (For code to be truly portable, NULL must be cast to the appropriate pointer
> type every time it is used since it is nothing more than an integer bit
> pattern :-)

If the smiley really means "I don't believe the statement that precedes the
smiley", OK; however, the statement in question is completely false.  Any valid
C compiler will turn

	foobar != 0

into a comparison of "foobar" with the appropriate representation for a null
pointer of type "pointer to Foobar".

> Why not just adopt a pattern of all zeroes as NULL instead?  Just ensure
> that this convention is observed by your malloc.  Then we can write
> 
> 	if (func)
> 		result = (*func)();
> 
> rather than using double negatives:
> 
> 	if (func != (int(*)(void))NULL)
> 		result = (*func)();

"if (func != (int(*)(void))NULL)" is equivalent to "if (func != NULL)", because
the compiler will perform the type conversion.  "if (func != NULL)" is
equivalent to "if (func != 0)", because "NULL" is supposed to be defined as "0"
(or "(void *)0", but the latter isn't really necessary).  "if (func != 0)" is
equivalent to "if (func)".  Therefore, you can write "if (func)" instead of
"if (func != (int(*)(void))NULL)" *regardless* of the representation of a null
pointer.

The confusion on this point probably results because C did not have function
prototypes until some compiler vendors put them in in anticipation of ANSI C.
Thus, while the compiler has enough information to know that NULL (or 0, it's
the same thing) in "foobar = NULL" or "if (func != NULL)" should be converted
to the appropriate pointer type, the compiler lacks enough information to do
the type conversion in "setbuf(stdout, NULL)" - it doesn't know that the second
argument to "setbuf" is "char *".  Therefore, you have to tell the compiler to
perform this conversion by explicitly casting the pointer:
"setbuf(stdout, (char *)NULL)".

If you have function prototypes in your C implementation, and have included the
proper include files or have otherwise arranged that prototypes are in scope
for all functions to which you refer, the compiler can do the conversion:

	void setbuf(FILE *stream, char *buf);

	...

	setbuf(stdout, NULL);

Please don't post a followup to this article unless you have good evidence that
the C *language* doesn't specify that the appropriate conversions be done in
the examples given.  Thank you.



More information about the Comp.lang.c mailing list