Skip site navigation (1)Skip section navigation (2)
Date:      Sat, 15 Feb 1997 10:17:23 +1100
From:      Giles Lean <giles@nemeton.com.au>
To:        Eivind Eklund <eivind@dimaga.com>
Cc:        hackers@freebsd.org
Subject:   Re: NULL as ((void*)0) (was Re: strlen() question) 
Message-ID:  <199702142317.KAA12738@nemeton.com.au>
In-Reply-To: <3.0.32.19970214173652.00c0b290@dimaga.com> 

next in thread | previous in thread | raw e-mail | index | archive | help
------- =_aaaaaaaaaa0
Content-Type: text/plain; charset="us-ascii"
Content-ID: <12731.855962183.1@nemeton.com.au>

On Fri, 14 Feb 1997 17:36:53 +0100  Eivind Eklund wrote:

> I hereby propose changing the default declaration of NULL under FreeBSD from
> #define NULL 0
>  to
> #define NULL ((void*)0)

This is more of a kludge than a good idea, and I offer the following
sage advice that I filed away many years ago.

Regards,

Giles


------- =_aaaaaaaaaa0
MIME-Version: 1.0
Content-Type: message/rfc822

>From rsalz@bbn.com Tue Nov  7 09:35:09 1989
Relay-Version: version Notes 2.8.2  87/11/24; site hpausla.aso.hp.com
From: rsalz@bbn.com (Rich Salz)
Date: Mon, 6 Nov 1989 22:35:09 GMT
Date-Received: Tue, 7 Nov 1989 13:37:34 GMT
Subject: Re: NULL
Message-ID: <2144@prune.bbn.com>
Organization: BBN Systems and Technologies Corporation
Path: hpausla!hpcuhc!hpda!hplabs!hp-sdd!ucsdhub!sdcsvax!network.ucsd.edu!ucsd!tut.cis.ohio-state.edu!ukma!mailrus!bbn!bbn.com!rsalz
Newsgroups: comp.os.minix
References: <4455@ast.cs.vu.nl>
Lines: 99

(This is part two of the set-up.  Andy and I have exchanged email about this.)

I believe that if you have anything other than "#define NULL 0" you
are encouraging sloppy non-portable code.  Yes, casting 0 all the time is
a pain, but c'est la vie.  Function prototypes help.

When it comes to this topic, Chris Torek is one of the many people who
are smarter than I am who are also better writers.  I'll let his old
words try to convince people:

>From bbn.com!bbn!mit-eddie!ll-xn!ames!umd5!mimsy!chris Thu Mar 10 15:37:10 EST 1988
Article 5474 of comp.lang.c:
Path: bbn.com!bbn!mit-eddie!ll-xn!ames!umd5!mimsy!chris
>From: chris@mimsy.UUCP (Chris Torek)
Newsgroups: comp.lang.c
Subject: Why NULL is 0
Summary: you have seen this before, but this one is for reference
Message-ID: <10576@mimsy.UUCP>
Date: 9 Mar 88 02:26:10 GMT
References: <2550049@hpisod2.HP.COM> <7412@brl-smoke.ARPA> <3351@chinet.UUCP> <10574@mimsy.UUCP>
Organization: U of Maryland, Dept. of Computer Science, Coll. Pk., MD 20742
Lines: 73

(You may wish to save this, keeping it handy to show to anyone who
claims `#define NULL 0 is wrong, it should be #define NULL <xyzzy>'.
I intend to do so, at any rate.)

Let us begin by postulating the existence of a machine and a compiler
for that machine.  This machine, which I will call a `Prime', or
sometimes `PR1ME', for obscure reasons such as the fact that it
exists, has two kinds of pointers.  `Character pointers', or objects
of type (char *), are 48 bits wide.  All other pointers, such as
(int *) and (double *), are 32 bits wide.

Now suppose we have the following C code:

 	main()
	{
 		f1(NULL);	/* wrong */
 		f2(NULL);	/* wrong */
 		exit(0);
 	}
 
 	f1(cp) char *cp; { if (cp != NULL) *cp = 'a'; }
 	f2(dp) double *dp; { if (dp != NULL) *dp = 2.2; }

There are two lines marked `wrong'.  Now suppose we were to define NULL
as 0.  Clearly both calls are then wrong: both pass `(int)0', when the
first should be a 48 bit (char *) nil pointer and the second a 32 bit
(double *) nil pointer.

Someone claims we can fix that by defining NULL as (char *)0.  Suppose
we do.  Then the first call is correct, but the second now passes a
48 bit (char *) nil pointer instead of a 32 bit (double *) nil pointer.
So much for that solution.

Ah, I hear another.  We should define NULL as (void *)0.  Suppose we
do.  Then at least one call is not correct, because one should pass
a 32 bit value and one a 48 bit value.  If (void *) is 48 bits, the
second is wrong; if it is 32 bits, the first is wrong.

Obviously there is no solution.  Or is there?  Suppose we change
the calls themselves, rather than the definition of NULL:

	main()
	{
		f1((char *)0);
		f2((double *)0);
		exit(0);
	}

Now both calls are correct, because the first passes a 48 bit (char *)
nil pointer, and the second a 32 bit (double *) nil pointer.  And
if we define NULL with

	#define NULL 0

we can then replace the two `0's with `NULL's:

	main()
	{
		f1((char *)NULL);
		f2((double *)NULL);
		exit(0);
	}

The preprocessor changes both NULLs to 0s, and the code remains
correct.

On a machine such as the hypothetical `Prime', there is no single
definition of NULL that will make uncasted, un-prototyped arguments
correct in all cases.  The C language provides a reasonable means
of making the arguments correct, but it is not via `#define'.
-- 
In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7163)
Domain:	chris@mimsy.umd.edu	Path:	uunet!mimsy!chris
-- 
Please send comp.sources.unix-related mail to rsalz@uunet.uu.net.
Use a domain-based address or give alternate paths, or you may lose out.

------- =_aaaaaaaaaa0
MIME-Version: 1.0
Content-Type: message/rfc822

>From henry@utzoo.uucp Wed Nov  8 05:35:40 1989
Relay-Version: version Notes 2.8.2  87/11/24; site hpausla.aso.hp.com
From: henry@utzoo.uucp (Henry Spencer)
Date: Tue, 7 Nov 1989 18:35:40 GMT
Date-Received: Thu, 9 Nov 1989 06:24:45 GMT
Subject: Re: NULL
Message-ID: <1989Nov7.183540.2486@utzoo.uucp>
Organization: U of Toronto Zoology
Path: hpausla!hpcuhc!hpda!motcsd!apple!usc!cs.utexas.edu!wuarchive!mailrus!jarvis.csri.toronto.edu!utgpu!utzoo!henry
Newsgroups: comp.os.minix
References: <4455@ast.cs.vu.nl> <2144@prune.bbn.com>
Lines: 30

In article <2144@prune.bbn.com> rsalz@bbn.com (Rich Salz) writes:
>I believe that if you have anything other than "#define NULL 0" you
>are encouraging sloppy non-portable code...

Unfortunately, there is a lot of sloppy non-portable code out there
already, and it is desirable to minimize code breakage where possible.
This is why ANSI C allows NULL to be any integer constant expression
equal to zero (e.g. `0' or `0L') or the same cast to `void *' (e.g.
`((void *)0)':  so that you can pick one that is the same size as the
pointers on your machine, to minimize breakage of badly-written code.
(The rationale for allowing the `void *' cast is that there may not
be an integer type the same size as the pointers.)  Of course, if
different pointers are different sizes, or the representation of null
pointers is strange, you are well and truly up the creek and there
is *no* definition that will avoid breakage.

Note that casting the zero to any *other* pointer type is illegal
and pointless.  (Although all manner of fudging may be necessary
in the presence of non-ANSI compilers.)

In any case, this stuff is a concession to badly-written code.  No
properly-written code under ANSI compilers will notice the difference
between the different forms of NULL.  In contexts where the desired
type is not known from context -- basically, function arguments in
the absence of a prototype or the presence of varargs -- NULL *must*
be cast to the proper pointer type.  Lazy programmers keep looking
for a way around this, but there simply *isn't any*.
-- 
A bit of tolerance is worth a  |     Henry Spencer at U of Toronto Zoology
megabyte of flaming.           | uunet!attcan!utzoo!henry henry@zoo.toronto.edu

------- =_aaaaaaaaaa0--



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