Skip site navigation (1)Skip section navigation (2)
Date:      19 Jan 1998 15:39:35 +0100
From:      dag-erli@ifi.uio.no (Dag-Erling Coidan Smørgrav)
To:        Terry Lambert <tlambert@primenet.com>
Cc:        asami@cs.berkeley.edu (Satoshi Asami), jamie@itribe.net, jdevale@ece.cmu.edu, hackers@FreeBSD.ORG
Subject:   Re: FreeBSD Netcards
Message-ID:  <xzp7m7wr0ig.fsf@hel.ifi.uio.no>
In-Reply-To: Terry Lambert's message of "Mon, 12 Jan 1998 18:19:47 %2B0000 (GMT)"
References:  <199801121819.LAA24153@usr04.primenet.com>

next in thread | previous in thread | raw e-mail | index | archive | help
Terry Lambert <tlambert@primenet.com> writes:
> If you:
> 
> 	#include <stdio.h>
> 
> 	main()
> 	{
> 		char    c = NULL;
> 
> 		printf( "The value of a NULL character is %d\n", c);
> 
> 		exit( 0);
> 	}
> 
> The program, when compiled, does not bitch about the "loss of precision"
> in the assignment in the declaration of 'c'.  8-).
> 
> The program, when run, displays:
> 
> 	The value of a NULL character is 0
> 
> I'd say the compiler says that 'c' is a "NULL character", until it
> bitches, properly, about the assignment.

Ahem.

Sorry for following up on a week-old article, but there's something
that needs straightening out here.

NULL is a macro that expands to 0. Some compiler vendors define it to
(void *)0 or whatever, which is useful in some cases, such as the
execl(2) functions, but mostly makes no difference.

Now the crux of the matter is that the ISO C standard states that 0 is
not only an integer literal, but also a pointer literal (or rather
*the* pointer literal, as all other literals need a cast to be used as
pointers) which translates into a bit pattern which is guaranteed not
to be a valid pointer on whichever platform you're working on. This is
why NULL expands to 0: because 0, when assigned to a pointer, means
NULL. Even if the NULL pointer, on your particular platform, is not
all bits zero, the literal 0 will translate to the NULL pointer.

Naturally, having the NULL pointer all bits zero (as it is on the
i386, and most modern architectures such as the motorola 68K, the MIPS
RISC or the Sun Sparc / UltraSparc) is very useful since global static
data is initialized to all bits zero upon startup, so global static
pointers are initialized to NULL.

Back to execl(2): this class of functions use varargs and expect their
argument lists to be terminated by a NULL pointer. Now, on a two's
complement platform with pointers and integers of the same size and an
all bits zero NULL pointer (i.e. any sane modern architecture) it
doesn't make a difference if the compiler thinks you're passing the
integer 0 or the NULL pointer, as their memory representation is
identical. However, on platforms where the NULL pointer is not all
bits zero, or where pointers are of a different size from integers,
the difference between the NULL pointer and the integer 0 is no longer
academic, and passing the integer 0 to execl(2) instead of a NULL
pointer can lead to unpleasant results. Finding the euphemism in the
previous sentence is left as an exercise to the reader. On those
platforms (which are more common than you might think - take the huge
memory model on an 80x86 in real mode, which has 16-bit integers but
32-bit pointers) it is useful to have NULL expand to (void *)0, as
this will force the compiler to interpret NULL as a pointer rather
than an integer.

Therefore, it is *perfectly correct* for the compiler to produce code
that outputs "The value of a NULL character is 0" from the source code
you give, since what you wrote is, in effect,

 		char    c = 0;

There is no loss of precision in the assignment, since you are
assigning a literal that fits well within 8 bits to an 8-bit signed
char. There is no type conversion needed. There is nothing fishy
whatsoever about the compiler's behaviour. If you're not convinced,
compile the source code with the -E switch (I assume you're using gcc)
which will dump the preprocessor output to stdout. You will see that
NULL expanded to 0. If you're still not convinced, feel free to
consult /usr/include/stdlib.h, the White Book, or, for that matter,
the text of the ISO C standard.

You also wrote the following

> Also, you're right that the standard allows for a non-zero value
> for NULL.  I don't know of anyone who uses this; it's pretty much
> in there to cause problems, like a lot of other "features", ie: the
> assumption that certain optimizations are allowable unless they are
> explicitly disallowed, etc..

The standard does *not* allow for a non-zero value of NULL. The
standard very clearly states that the literal 0, when assigned to a
pointer variable, evaluates to a NULL pointer. The standard does not,
however, say anything about the internal representation of a NULL
pointer. Wether or not a NULL pointer is all bits zero is
implementation-defined.

> So pretty much until you fix the compiler, I'm going to keep my
> terminology as fuzzy as the compiler's enforcement.

Again, nothing is wrong with the compiler. It's in perfect accordance
with the ISO C standard. Your use of NULL in "a NULL string" or "the
NULL character", however, is definitely incorrect. Instead, you should
use "an empty string" and "the NUL character" (only one L),
respectively.

Hope this cleared up what I consider a rather fundamental misunder-
standing of C pointer mechanics.

-- 
 * Finrod (INTJ) * Unix weenie * dag-erli@ifi.uio.no * cellular +47-92835919 *
  RFC1123: "Be liberal in what you accept, and conservative in what you send"



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?xzp7m7wr0ig.fsf>