"do ... while ((NULL + 1) - 1);" -- valid C?

Chris Torek chris at mimsy.UUCP
Fri Aug 11 19:01:17 AEST 1989


In article <940 at lakesys.UUCP> chad at lakesys.UUCP (D. Chadwick Gibbons) writes:
>... In most decent implementations, NULL is indeed defined as either
>0 or 0L.

Right.

>But this can't be, and isn't, true in all implementations,

No and yes: it could be, but it is not.

>... In many current implmentations, NULL is often defined as ((char *)0)
>since it is the only "safe" thing to do [meaning `the only way the vendor
>can keep the authors of bad code happy'].

This is both unsafe and wrong, even if it does keep such authors happy.
Consider:  If we write

	char *cp;
	int *ip;

	ip = cp;

the compiler must issue some kind of diagnostic (it says so in the
proposed ANSI C specification, and it says in K&R-1 that this operation
is machine-dependent, and all quality compilers do indeed generate a
warning).  This situation does not change if we write

	ip = (char *)ip;

It does change if we write instead

	ip = (int *)(char *)ip;

which puts the value in ip through two transformations (from
pointer-to-int to pointer-to-char, then from pointer-to-char to
pointer-to-int), and these two together are required to reproduce
the original value (this is something of a special case).

So: consider what happens if some implementer has wrongly put the line

	#define NULL ((char *)0)

in <stdio.h> and <stdarg.h> and so forth, and we write

	ip = NULL;

The compiler sees

	ip = ((char *)0);

which, as far as the type system is concerned, is identical to

	ip = cp;

---that is, it is machine dependent, and requires a warning.  We can
(probably) eliminate the warning% by adding a cast:

	ip = (int *)NULL;

which expands to

	ip = (int *)((char *)0);

On *most* machines, this `just happens' to work.  But if we look very
closely at the language definition, we find that it is not *required*
to work.  The version of this that is required to work is instead

	ip = (int *)(char *)(int *)0;

We are not allowed (outside of machine-dependent code) to change a
pointer-to-char into a pointer-to-int unless the pointer-to-char itself
came into existence as the result of a cast from a pointer-to-int.  The
only way to *create* a nil-pointer-to-int in the first place is to
write (int *)0, or (in the proposed ANSI C) (int *)(void *)0.

Of course, the actual definition of NULL in <stdio.h> and <stdarg.h>
and so on is provided per machine, so if

	int *ip = (int *)(char *)0;

`just happens' to work on that machine, the vendor could get away
with it.  But

	int *ip = NULL;

is guaranteed to work *without* generating warnings on any machine
where NULL is correctly defined, and one should not have to write

	int *ip = (int *)NULL;

just to avoid getting warnings---nor should the compiler be silent
about code like

	int *ip; char *cp; ip = cp;

The rest of <940 at lakesys.UUCP> is correct.
-----
% The (probably) in eliminating warnings refers to the fact that
  a compiler can warn about anything it pleases:

	% cc -o foo foo.c
	cc: Warning: relative humidity and barometer pressure
		indicate that thunderstorms are likely
	cc: Warning: your shoelace is untied
	cc: Warning: this code looks ugly
	cc: Warning: your mother wears army boots
	cc: Warning: Hey!  Keep away from me with that axe!
	cc: Warning: Ack!  No, wait, I di(*&1to01llk
-- 
In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7163)
Domain:	chris at mimsy.umd.edu	Path:	uunet!mimsy!chris



More information about the Comp.lang.c mailing list