Section 1. Null Pointers
1.1: What is this infamous null pointer, anyway?
The language definition states that for each pointer type, there is a
special value -- the "null pointer" -- which is distinguishable from all
other pointer values and which is not the address of any object or
function.
That is, the address-of operator & will never yield a null pointer, nor
will a successful call to malloc. (malloc returns a null pointer when it
fails, and this is a typical use of null pointers: as a "special" pointer
value with some other meaning, usually "not allocated" or "not pointing
anywhere yet.")
A null pointer is conceptually different from an uninitialized pointer. A
null pointer is known not to point to any object; an uninitialized pointer
might point anywhere. See also questions 3.1,
3.13, and 17.1.
As mentioned in the definition above, there is a null pointer for each
pointer type, and the internal values of null pointers for different types
may be different. Although programmers need not know the internal values,
the compiler must always be informed which type of null pointer is
required,
so it can make the distinction if necessary (see below).
References: K&R I Sec. 5.4 pp. 97-8; K&R II Sec. 5.4 p. 102; H&S Sec. 5.3 p.
91; ANSI Sec. 3.2.2.3 p. 38.
1.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)
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. For instance, the Unix system
call "execl" takes a variable-length, null-pointer-terminated list of
character pointer arguments. To generate a null pointer in a function call
context, an explicit cast is typically required, to force the 0 to be in a
pointer context:
execl("/bin/sh", "sh", "-c", "ls", (char *)0);
If the (char *) cast 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 unadorned 0's. Function prototypes cannot
provide the types for variable arguments in variable-length argument lists,
however, so explicit casts are still required for those arguments. It is
safest always to cast null pointer function arguments, 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:
initialization
assignment
comparison
function call, prototype in scope, fixed argument
Explicit cast required:
function call, no prototype in scope
variable argument in varargs function call
References: K&R I Sec. A7.7 p. 190, Sec. A7.14 p. 192; K&R II Sec. A7.10 p.
207, Sec. A7.17 p. 209; H&S Sec. 4.6.3 p. 72; ANSI Sec. 3.2.2.3 .
1.3: What is NULL and how is it #defined?
As a matter of style, many people prefer not to have unadorned 0's
scattered
throughout their programs. For this reason, the preprocessor macro NULL is
#defined (by or ), with value 0 (or (void *)0, about which more later).
A programmer who wishes to make explicit the distinction between 0 the
integer and 0 the null pointer can then use NULL whenever a null pointer is
required. This is a stylistic convention only; the preprocessor turns NULL
back to 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 1.2 above
applies
for NULL as well as 0.)
NULL should only be used for pointers; see question 1.8.
References: K&R I Sec. 5.4 pp. 97-8; K&R II Sec. 5.4 p. 102; H&S Sec. 13.1
p. 283; ANSI Sec. 4.1.5 p. 99, Sec. 3.2.2.3 p. 38, Rationale
Sec. 4.1.5 p. 74.
1.4: How should NULL be #defined on a machine which uses a nonzero bit
pattern as the internal representation of a null pointer?
Programmers should never need to know the internal representation(s) of
null
pointers, because they are normally taken care of by the compiler. If a
machine uses a nonzero bit pattern for null pointers, it is the compiler's
responsibility to generate it when the programmer requests, by writing "0"
or "NULL," a null pointer. Therefore, #defining NULL as 0 on a machine for
which internal null pointers are nonzero is as valid as on any other,
because the compiler must (and can) still generate the machine's correct
null pointers in response to unadorned 0's seen in pointer contexts.
1.5: 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 to work correctly, but pointer arguments
to
other types would still be problematical, and legal constructions such as
FILE *fp = NULL;
could fail.
Nevertheless, ANSI C allows the alternate
#define NULL ((void *)0)
definition for NULL. Besides 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 1.8).
References: ANSI Rationale Sec. 4.1.5 p. 74.
1.6: I use the preprocessor macro
#define Nullptr(type) (type *)0
to help me build null pointers of the correct type.
This trick, though popular in some circles, does not buy much. It is not
needed in assignments and comparisons; see question 1.2. It does not even
save keystrokes. Its use suggests to the reader that the author is shaky
on
the subject of null pointers, and requires the reader to check the
#definition of the macro, its invocations, and all other pointer usages
much
more carefully. See also question 8.1.
1.7: Is the abbreviated pointer comparison "if(p)" to test for non- null
pointers valid? What if the internal representation for null pointers is
nonzero?
When C requires the boolean value of an expression (in the if, while, for,
and do statements, and with the &&, ||, !, and ?: operators), a false value
is produced when the expression compares equal to zero, and a true value
otherwise. That is, whenever one writes
if(expr)
where "expr" is any expression at all, the compiler essentially acts as if
it had been written as
if(expr != 0)
Substituting the trivial pointer expression "p" for "expr," we have
if(p) is equivalent to if(p != 0)
and this is a comparison context, so the compiler can tell that the
(implicit) 0 is a null pointer, and use the correct value. There is no
trickery involved here; compilers do work this way, and generate identical
code for both statements. The internal representation of a pointer does not
matter.
The boolean negation operator, !, can be described as follows:
!expr is essentially equivalent to expr?0:1
It is left as an exercise for the reader to show that
if(!p) is equivalent to if(p == 0)
"Abbreviations" such as if(p), though perfectly legal, are considered by
some to be bad style.
See also question 8.2.
References: K&R II Sec. A7.4.7 p. 204; H&S Sec. 5.3 p. 91; ANSI Secs.
3.3.3.3, 3.3.9, 3.3.13, 3.3.14, 3.3.15, 3.6.4.1, and 3.6.5 .
1.8: If "NULL" and "0" are equivalent, which should I use?
Many programmers believe that "NULL" should be used in all pointer contexts,
as a reminder that the value is to be thought of as a pointer. Others feel
that the confusion surrounding "NULL" and "0" is only compounded by hiding
"0" behind a #definition, and prefer to use unadorned "0" instead. There is
no one right answer. C programmers must understand that "NULL" and "0" are
interchangeable and that an uncast "0" is perfectly acceptable in
initialization, assignment, and comparison contexts. Any usage of "NULL"
(as opposed to "0") should be considered a gentle reminder that a pointer is
involved; programmers should not depend on it (either for their own
understanding
or
the compiler's) for distinguishing pointer 0's from integer 0's.
NULL should not be used when another kind of 0 is required, even though it
might work, because doing so sends the wrong stylistic message. (ANSI
allows the #definition of NULL to be (void *)0, which will not work in non
-pointer contexts.) In particular, do not use NULL when the ASCII null
character (NUL) is desired. Provide your own definition
#define NUL '\0'
if you must.
References: K&R II Sec. 5.4 p. 102.
1.9: But wouldn't it be better to use NULL (rather than 0) in case the value
of NULL changes, perhaps on a machine with nonzero null pointers?
No. Although symbolic constants are often used in place of numbers because
the numbers might change, this is not the reason that NULL is used in place
of 0. Once again, the language guarantees that source-code 0's (in pointer
contexts) generate null pointers. NULL is used only as a stylistic
convention.
1.10: I'm confused. NULL is guaranteed to be 0, but the null pointer is
not?
When the term "null" or "NULL" is casually used, one of several things may
be meant:
The conceptual null pointer, the abstract language concept defined in
question 1.1. It is implemented with...
The internal (or run-time) representation of a null pointer, which may
or may not be all-bits-0 and which may be different for different pointer
types. The actual values should be of concern only to compiler writers.
Authors of C programs never see them, since they use...
The source code syntax for null pointers, which is the single character
"0". It is often hidden behind...
The NULL macro, which is #defined to be "0" or "(void *)0". Finally, as
red herrings, we have...
The ASCII null character (NUL), which does have all bits zero, but has
no necessary relation to the null pointer except in name; and...
The "null string," which is another name for an empty string (""). The
term "null string" can be confusing in C (and should perhaps be avoided),
because it involves a null ('\0') character, but not a null pointer, which
brings us full circle...
This article always uses the phrase "null pointer" (in lower case) for
sense
1, the character "0" for sense 3, and the capitalized word "NULL" for sense
4.
1.11: Why is there so much confusion surrounding null pointers? Why do
these questions come up so often?
C programmers traditionally like to know more than they need to about the
underlying machine implementation. The fact that null pointers are
represented both in source code, and internally to most machines, as zero
invites unwarranted assumptions. The use of a preprocessor macro (NULL)
suggests that the value might change later, or on some weird machine. The
construct "if(p == 0)" is easily misread as calling for conversion of p to
an integral type, rather than 0 to a pointer type, before the comparison.
Finally, the distinction between the several uses of the term "null"
(listed
above) is often overlooked.
One good way to wade out of the confusion is to imagine that C had a
keyword
(perhaps "nil", like Pascal) with which null pointers were requested. The
compiler could either turn "nil" into the correct type of null pointer,
when
it could determine the type from the source code, or complain when it could
not. Now, in fact, in C the keyword for a null pointer is not "nil" but
"0",
which works almost as well, except that an uncast "0" in a non-pointer
context generates an integer zero instead of an error message, and if that
uncast 0 was supposed to be a null pointer, the code may not work.
1.12: I'm still confused. I just can't understand all this null pointer
stuff.
Follow these two simple rules:
When you want to refer to a null pointer in source code, use "0" or
"NULL".
If the usage of "0" or "NULL" is an argument in a function call, cast
it
to the pointer type expected by the function being called.
The rest of the discussion has to do with other people's misunderstandings,
or with the internal representation of null pointers (which you shouldn't
need to know), or with ANSI C refinements. Understand questions 1.1, 1.2,
and 1.3, and consider 1.8 and 1.11, and you'll do fine.
1.13: Given all the confusion surrounding null pointers, wouldn't it be
easier simply to require them to be represented internally by zeroes?
If for no other reason, doing so would be ill-advised because it would
unnecessarily constrain implementations which would otherwise naturally
represent null pointers by special, nonzero bit patterns, particularly when
those values would trigger automatic hardware traps for invalid accesses.
Besides, what would this requirement really accomplish? Proper
understanding of null pointers does not require knowledge of the internal
representation, whether zero or nonzero. Assuming that null pointers are
internally zero does not make any code easier to write (except for a
certain
ill-advised usage of calloc; see question 3.13). Known-zero internal
pointers would not obviate casts in function calls, because the size of the
pointer might still be different from that of an int. (If "nil" were used
to request null pointers rather than "0," as mentioned in question 1.11,
the
urge to assume an internal zero representation would not even arise.)
1.14: Seriously, have any actual machines really used nonzero null
pointers,
or different representations for pointers to different types?
The Prime 50 series used segment 07777, offset 0 for the null pointer, at
least for PL/I. Later models used segment 0, offset 0 for null pointers in
C, necessitating new instructions such as TCNP (Test C Null Pointer),
evidently as a sop to all the extant poorly-written C code which made
incorrect assumptions. Older, word-addressed Prime machines were also
notorious for requiring larger byte pointers (char *'s) than word pointers
(int *'s).
The Eclipse MV series from Data General has three architecturally supported
pointer formats (word, byte, and bit pointers), two of which are used by C
compilers: byte pointers for char * and void *, and word pointers for
everything else.
Some Honeywell-Bull mainframes use the bit pattern 06000 for (internal)
null
pointers.
The CDC Cyber 180 Series has 48-bit pointers consisting of a ring, segment,
and offset. Most users (in ring 11) have null pointers of 0xB00000000000.
The Symbolics Lisp Machine, a tagged architecture, does not even have
conventional numeric pointers; it uses the pair (basically a nonexistent