5. Null Pointers

comp.lang.c FAQ list · Question 5.1

Q: What is this infamous null pointer, anyway?


A: 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 ``guaranteed to compare unequal to a pointer to any object or function.'' That is, a null pointer points definitively nowhere; it is not the address of any object or function. The address-of operator & will never yield a null pointer, nor will a successful call to malloc.[footnote] (malloc does return 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 or function; an uninitialized pointer might point anywhere. See also questions 1.30, 7.1, and 7.31.

As mentioned 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 that it can make the distinction if necessary (see questions 5.2, 5.5, and 5.6).

References: K&R1 Sec. 5.4 pp. 97-8
K&R2 Sec. 5.4 p. 102
ISO Sec. 6.2.2.3
Rationale Sec. 3.2.2.3
H&S Sec. 5.3.2 pp. 121-3




comp.lang.c FAQ list · Question 5.2

Q: How do I get a null pointer in my programs?


A: With a null pointer constant.

According to the language definition, an ``integral constant expression with the value 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; see also question 5.11.)

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 probably safest to properly cast all null pointer constants in function calls, to guard against varargs functions or those without prototypes.

Here is a summary of the rules for when null pointer constants may be used by themselves, and when they require explicit casts:

[TABLE GOES HERE]

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
ISO Sec. 6.2.2.3
H&S Sec. 4.6.3 p. 95, Sec. 6.2.7 p. 171




comp.lang.c FAQ list · Question 5.3

Q: Is the abbreviated pointer comparison ``if(p)'' to test for non-null pointers valid? What if the internal representation for null pointers is nonzero?


A: It is always valid.

When C requires the Boolean value of an expression, a false value is inferred 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 actually a null pointer constant, and use the correct null pointer value. There is no trickery involved here; compilers do work this way, and generate identical code for both constructs. The internal representation of a null pointer does not matter.

The boolean negation operator, !, can be described as follows:

	!expr	is essentially equivalent to	(expr)?0:1
		or to	((expr) == 0)
which leads to the conclusion that
	if(!p)	is equivalent to	if(p == 0)

``Abbreviations'' such as if(p), though perfectly legal[footnote] , are considered by some to be bad style (and by others to be good style; see question 17.10).

See also question 9.2.

References: K&R2 Sec. A7.4.7 p. 204
ISO Sec. 6.3.3.3, Sec. 6.3.9, Sec. 6.3.13, Sec. 6.3.14, Sec. 6.3.15, Sec. 6.6.4.1, Sec. 6.6.5
H&S Sec. 5.3.2 p. 122




comp.lang.c FAQ list · Question 5.4

Q: What is NULL and how is it defined?


A: As a matter of style, many programmers prefer not to have unadorned 0's scattered through their programs, some representing numbers and some representing pointers. Therefore, the preprocessor macro NULL is defined (by several headers, including <stdio.h> and <stddef.h>) as a null pointer constant, typically 0 or ((void *)0) (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 be used only as a pointer constant; see question 5.9.

References: K&R1 Sec. 5.4 pp. 97-8
K&R2 Sec. 5.4 p. 102
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




comp.lang.c FAQ list · Question 5.5

Q: How should NULL be defined on a machine which uses a nonzero bit pattern as the internal representation of a null pointer?


A: The same as on any other machine: as 0 (or some version of 0; see question 5.4).

Whenever a programmer requests a null pointer, either by writing ``0'' or ``NULL'', it is the compiler's responsibility to generate whatever bit pattern the machine uses for that null pointer. (Again, the compiler can tell that an unadorned 0 requests a null pointer when the 0 is in a pointer context; see question 5.2.) Therefore, #defining NULL as 0 on a machine for which internal null pointers are nonzero is as valid as on any other: the compiler must always be able to generate the machine's correct null pointers in response to unadorned 0's seen in pointer contexts. A constant 0 is a null pointer constant; NULL is just a convenient name for it (see also question 5.13).

(Section 4.1.5 of the C Standard states that NULL ``expands to an implementation-defined null pointer constant,'' which means that the implementation gets to choose which form of 0 to use and whether to use a void * cast; see questions 5.6 and 5.7. ``Implementation-defined'' here does not mean that NULL might be #defined to match some implementation-specific nonzero internal null pointer value.)

See also questions 5.2, 5.10 and 5.17.

References: ISO Sec. 7.1.6
Rationale Sec. 4.1.5




comp.lang.c FAQ list · Question 5.6

Q: If NULL were defined as follows:

	#define NULL ((char *)0)
wouldn't that make function calls which pass an uncast NULL work?


A: Not in the most general case. The complication 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 could still (in the absence of prototypes) require explicit casts. Furthermore, legal constructions such as

	FILE *fp = NULL;
could fail.

Nevertheless, ANSI C allows the alternate definition

	#define NULL ((void *)0)
for NULL. [footnote] 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). See also question 5.7.

At any rate, ANSI function prototypes ensure that most (though not quite all; see question 5.2) pointer arguments are converted correctly when passed as function arguments, so the question is largely moot.

Programmers who are accustomed to modern, ``flat'' memory architectures may find the idea of ``different kinds of pointers'' very difficult to accept. See question 5.17 for some examples.

References: Rationale Sec. 4.1.5




comp.lang.c FAQ list · Question 5.7

Q: My vendor provides header files that #define NULL as 0L. Why?


A: Some programs carelessly attempt to generate null pointers by using the NULL macro, without casts, in non-pointer contexts. (Doing so is not guaranteed to work; see questions 5.2 and 5.11.) On machines which have pointers larger than integers (such as PC compatibles in ``large'' model; see also question 5.17), a particular definition of NULL such as 0L can help these incorrect programs to work. (0L is a perfectly valid definition of NULL; it is an ``integral constant expression with value 0.'') Whether it is wise to coddle incorrect programs is debatable; see also question 5.6 and section 17.

References: Rationale Sec. 4.1.5
H&S Sec. 5.3.2 pp. 121-2




comp.lang.c FAQ list · Question 5.8

Q: Is NULL valid for pointers to functions?


A: Yes (but see question 4.13).

References: ISO Sec. 6.2.2.3




comp.lang.c FAQ list · Question 5.9

Q: If NULL and 0 are equivalent as null pointer constants, which should I use?


A: 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 macro, and prefer to use unadorned 0 instead. There is no one right answer. (See also questions 9.4 and 17.10.) C programmers must understand that NULL and 0 are interchangeable in pointer contexts, and that an uncast 0 is perfectly acceptable. 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.

It is only in pointer contexts that NULL and 0 are equivalent. 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. (Furthermore, ANSI allows the definition of NULL to be ((void *)0), which will not work at all 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&R1 Sec. 5.4 pp. 97-8
K&R2 Sec. 5.4 p. 102




comp.lang.c FAQ list · Question 5.10

Q: 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 internal null pointers?


A: No. (Using NULL may be preferable, but not for this reason.) 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. See questions 5.5 and 9.4.




comp.lang.c FAQ list · Question 5.11

Q: I once used a compiler that wouldn't work unless NULL was used.


A: Unless the code being compiled was nonportable, that compiler was probably broken.

Perhaps the code used something like this nonportable version of an example from question 5.2:

	execl("/bin/sh", "sh", "-c", "date", NULL);	/* WRONG */
Under a compiler which defines NULL to ((void *)0) (see question 5.6), this code will happen to work. [footnote] However, if pointers and integers have different sizes or representations, the (equally incorrect) code
	execl("/bin/sh", "sh", "-c", "date", 0);	/* WRONG */
may not work.

Correct, portable code uses an explicit cast:

	execl("/bin/sh", "sh", "-c", "date", (char *)NULL);
With the cast, the code works correctly no matter what the machine's integer and pointer representations are, and no matter which form of null pointer constant the compiler has chosen as the definition of NULL. (The code fragment in question 5.2, which used 0 instead of NULL, is equally correct; see also question 5.9.) (In general, making decisions about a language based on the behavior of one particular compiler is likely to be counterproductive.)


comp.lang.c FAQ list · Question 5.12

Q: I use the preprocessor macro

#define Nullptr(type) (type *)0
to help me build null pointers of the correct type.


A: This trick, though popular and superficially attractive, does not buy much. It is not needed in assignments or comparisons; see question 5.2. (It does not even save keystrokes.) See also questions 9.1 and 10.2.




comp.lang.c FAQ list · Question 5.13

Q: This is strange. NULL is guaranteed to be 0, but the null pointer is not?


A: When the term ``null'' or ``NULL'' is casually used, one of several things may be meant:

  1. The conceptual null pointer, the abstract language concept defined in question 5.1. It is implemented with...
  2. 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...
  3. The null pointer constant, which is a constant integer 0 [footnote] (see question 5.2). It is often hidden behind...
  4. The NULL macro, which is #defined to be 0 (see question 5.4). Finally, as red herrings, we have...
  5. The ASCII null character (NUL), which does have all bits zero, but has no necessary relation to the null pointer except in name; and...
  6. The ``null string,'' which is another name for the empty string (""). Using the term ``null string'' can be confusing in C, because an empty string involves a null ('\0') character, but not a null pointer, which brings us full circle...

In other words, to paraphrase the White Knight's description of his song in Through the Looking-Glass, the name of the null pointer is ``0'', but the name of the null pointer is called ``NULL'' (and we're not sure what the null pointer is).

This document uses the phrase ``null pointer'' (in lower case) for sense 1, the token ``0'' or the phrase ``null pointer constant'' for sense 3, and the capitalized word ``NULL'' for sense 4.[footnote]

Additional links: mnemonic device

References: H&S Sec. 1.3 p. 325
Through the Looking-Glass, chapter VIII.




comp.lang.c FAQ list · Question 5.14

Q: Why is there so much confusion surrounding null pointers? Why do these questions come up so often?


A: C programmers traditionally like to know a lot (perhaps 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) may seem to suggest that the value could change some day, 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 in question 5.13) is often overlooked.

One good way to wade out of the confusion is to imagine that C used a keyword (perhaps nil, like Pascal) as a null pointer constant. The compiler could either turn nil into the appropriate type of null pointer when it could unambiguously determine that type from the source code, or complain when it could not. Now in fact, in C the keyword for a null pointer constant 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 constant, the resulting program may not work.

Additional links: an article by Richard Stamp with another angle on the NULL/0 distinction




comp.lang.c FAQ list · Question 5.15

Q: I'm confused. I just can't understand all this null pointer stuff.


A: Here are two simple rules you can follow:

  1. When you want a null pointer constant in source code, use ``0'' or ``NULL''.
  2. 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, with the internal representation of null pointers (which you shouldn't need to know), and with the complexities of function prototypes. (Taking those complexities into account, we find that rule 2 is conservative[footnote] , of course; but it doesn't hurt.) Understand questions 5.1, 5.2, and 5.4, and consider 5.3, 5.9, 5.13, and 5.14, and you'll do fine.




comp.lang.c FAQ list · Question 5.16

Q: Given all the confusion surrounding null pointers, wouldn't it be easier simply to require them to be represented internally by zeroes?


A: Some implementations naturally represent null pointers by special, nonzero bit patterns, particularly when it can be arranged that inadvertently using those values triggers automatic hardware traps. Requiring null pointers to be represented internally as 0, and therefore disallowing use of the special, nonzero values, would be an unfortunate step backwards, because catching errors which result in invalid accesses is a Good Thing.

Besides, what would such a 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 7.31). Known-zero internal pointers would not reduce the need for 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, as mentioned in question 5.14, the urge to assume an internal zero representation would not even arise.)




comp.lang.c FAQ list · Question 5.17

Q: Seriously, have any actual machines really used nonzero null pointers, or different representations for pointers to different types?


A: 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 [footnote] 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. For historical reasons during the evolution of the 32-bit MV line from the 16-bit Nova line, word pointers and byte pointers had the offset, indirection, and ring protection bits in different places in the word. Passing a mismatched pointer format to a function resulted in protection faults. Eventually, the MV C compiler added many compatibility options to try to deal with code that had pointer type mismatch errors.

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. It was common on old CDC ones-complement machines to use an all-one-bits word as a special flag for all kinds of data, including invalid addresses.

The old HP 3000 series uses a different addressing scheme for byte addresses than for word addresses; like several of the machines above it therefore uses different representations for char * and void * pointers than for other pointers.

The Symbolics Lisp Machine, a tagged architecture, does not even have conventional numeric pointers; it uses the pair <NIL, 0> (basically a nonexistent <object, offset> handle) as a C null pointer.

Depending on the ``memory model'' in use, 8086-family processors (PC compatibles) may use 16-bit data pointers and 32-bit function pointers, or vice versa.

Some 64-bit Cray machines represent int * in the lower 48 bits of a word; char * additionally uses some of the upper 16 bits to indicate a byte address within a word.

Additional links: A message from Chris Torek with more details about some of these machines.

References: K&R1 Sec. A14.4 p. 211




comp.lang.c FAQ list · Question 5.18

Q: Is a run-time integral value of 0, cast to a pointer, guaranteed to be a null pointer?


A: No. Only constant integral expressions with value 0 are guaranteed to indicate null pointers. See also questions 4.14, 5.2, and 5.19.




comp.lang.c FAQ list · Question 5.19

Q: How can I access an interrupt vector located at the machine's location 0? If I set a pointer to 0, the compiler might translate it to some nonzero internal null pointer value.


A: Since whatever is at location 0 is obviously machine dependent, you're free to use whatever machine-dependent trick will work to get there. Read your vendor's documentation (and see section 19). It's likely that if it's at all meaningful for you to be accessing location 0, the system will be set up to make it reasonably easy to do so. Some possibilities are:

  1. Simply set a pointer to 0. (This is the way that doesn't have to work, but if it's meaningful, it probably will.)
  2. Assign the integer 0 to an int variable, and convert that int to a pointer. (This is also not guaranteed to work, but it probably will.)
  3. Use a union to set the bits of a pointer variable to 0:
    	union {
    		int *u_p;
    		int u_i;	/* assumes sizeof(int) >= sizeof(int *) */
    	} p;
    
    	p.u_i = 0;
    
  4. Use memset to set the bits of a pointer variable to 0:
    	memset((void *)&p, 0, sizeof(p));
    
  5. Declare an external variable or array
    	extern int location0;
    
    and use an assembly language file, or some special linker invocation, to arrange that this symbol refers to (i.e. the variable is placed at) address 0.

See also questions 4.14 and 19.25.

References: K&R1 Sec. A14.4 p. 210
K&R2 Sec. A6.6 p. 199
ISO Sec. 6.3.4
Rationale Sec. 3.3.4
H&S Sec. 6.2.7 pp. 171-2




comp.lang.c FAQ list · Question 5.20

Q: What does a run-time ``null pointer assignment'' error mean? How can I track it down?


A: This message, which typically occurs with MS-DOS compilers, means that you've written, via a null pointer, to an invalid location--probably offset 0 in the default data segment. (The pointer in question might have been uninitialized, although as we saw in question 1.30, not all uninitialized pointers necessarily start out as null pointers.)

A debugger may let you set some kind of data watchpoint on location 0. Alternatively, you could write a bit of code to stash away a copy of 20 or so bytes from location 0, and periodically check that the memory at location 0 hasn't changed. See also question 16.8.





Read sequentially: prev next up



about this FAQ list   about eskimo   search   feedback   copyright

Hosted by Eskimo North