Definition of NULL. (fwd)
Joel Sherrill
joel@OARcorp.com
Mon Apr 27 09:53:00 GMT 1998
An RTEMS user recently noticed that the newlib definitions of NULL are not
proper. The proper definition is apparently "0" or "(void *)0". The
offending definitions are in:
stdio.h:#define NULL 0L
stdlib.h:#define NULL 0L
string.h:#define NULL 0L
time.h:#define NULL 0L
In newlib/libc/include. There may be others in machine and OS specific
files.
I have included the referenced FAQ sections with the flame bait deleted.
:)
Thanks.
--joel
----------------------------
Please see the C-FAQ.
The correct definition for NULL is
0
or
((void *)0)
I've included the salient sections of the C-FAQ
( http://www.eskimo.com/~scs/C-faq/faq.html ).
=========================================================================
Question 5.2
How do I get a null pointer in my programs?
According to the language definition, a constant 0 in a pointer
context is converted into a null pointer at compile time. That is, in
an initialization, assignment, or comparison when one side is a
variable or expression of pointer type, the compiler can tell that a
constant 0 on the other side requests a null pointer, and generate
the correctly-typed null pointer value. Therefore, the following
fragments are perfectly legal:
char *p = 0;
if(p != 0)
(See also question 5.3.)
However, an argument being passed to a function is not necessarily
recognizable as a pointer context, and the compiler may not be able
to tell that an unadorned 0 ``means'' a null pointer. To generate a
null pointer in a function call context, an explicit cast may be
required, to force the 0 to be recognized as a pointer. For example,
the Unix system call execl takes a variable-length,
null-pointer-terminated list of character pointer arguments, and is
correctly called like this:
execl("/bin/sh", "sh", "-c", "date", (char *)0);
If the (char *) cast on the last argument were omitted, the compiler
would not know to pass a null pointer, and would pass an integer 0
instead. (Note that many Unix manuals get this example wrong .)
When function prototypes are in scope, argument passing becomes an
``assignment context,'' and most casts may safely be omitted, since
the prototype tells the compiler that a pointer is required, and of
which type, enabling it to correctly convert an unadorned 0. Function
prototypes cannot provide the types for variable arguments in
variable-length argument lists however, so explicit casts are still
required for those arguments. (See also question 15.3.) It is safest
to properly cast all null pointer constants in function calls: to
guard against varargs functions or those without prototypes, to allow
interim use of non-ANSI compilers, and to demonstrate that you know
what you are doing. (Incidentally, it's also a simpler rule to
remember.)
Summary:
Unadorned 0 okay: Explicit cast required:
initialization function call,
no prototype in scope
assignment
variable argument in
comparison varargs function call
function call,
prototype in scope,
fixed argument
References: K&R1 Sec. A7.7 p. 190, Sec. A7.14 p. 192
K&R2 Sec. A7.10 p. 207, Sec. A7.17 p. 209
ANSI Sec. 3.2.2.3
ISO Sec. 6.2.2.3
H&S Sec. 4.6.3 p. 95, Sec. 6.2.7 p. 171
======================================================================
Question 5.4
What is NULL and how is it #defined?
As a matter of style, many programmers prefer not to have unadorned
0's scattered through their programs. Therefore, the preprocessor
macro NULL is #defined (by <stdio.h> or <stddef.h>) with the value 0,
possibly cast to (void *) (see also question 5.6). A programmer who
wishes to make explicit the distinction between 0 the integer and 0
the null pointer constant can then use NULL whenever a null pointer
is required.
Using NULL is a stylistic convention only; the preprocessor turns
NULL back into 0 which is then recognized by the compiler, in pointer
contexts, as before. In particular, a cast may still be necessary
before NULL (as before 0) in a function call argument. The table
under question 5.2 above applies for NULL as well as 0 (an unadorned
NULL is equivalent to an unadorned 0).
NULL should only be used for pointers; see question 5.9.
References: K&R1 Sec. 5.4 pp. 97-8
K&R2 Sec. 5.4 p. 102
ANSI Sec. 4.1.5, Sec. 3.2.2.3
ISO Sec. 7.1.6, Sec. 6.2.2.3
Rationale Sec. 4.1.5
H&S Sec. 5.3.2 p. 122, Sec. 11.1 p. 292
======================================================================Question
5.6
If NULL were defined as follows:
#define NULL ((char *)0)
wouldn't that make function calls which pass an uncast NULL work?
Not in general. The problem is that there are machines which use
different internal representations for pointers to different types of
data. The suggested definition would make uncast NULL arguments to
functions expecting pointers to characters work correctly, but
pointer arguments of other types would still be problematical, and
legal constructions such as
FILE *fp = NULL;
could fail.
Nevertheless, ANSI C allows the alternate definition
#define NULL ((void *)0)
for NULL. Besides potentially helping incorrect programs to work
(but only on machines with homogeneous pointers, thus questionably
valid assistance), this definition may catch programs which use NULL
incorrectly (e.g. when the ASCII NUL character was really intended;
see question 5.9).
References: Rationale Sec. 4.1.5
==========================================================================
---
Eric Norum eric@skatter.usask.ca
Saskatchewan Accelerator Laboratory Phone: (306) 966-6308
University of Saskatchewan FAX: (306) 966-6058
Saskatoon, Canada.
More information about the Newlib
mailing list