Reputation: 955
I was interviewing a guy for a mid-level software engineering position yesterday, and he mentioned that in C, NULL is not always zero and that he had seen implementations of C where NULL is not zero. I find this highly suspect, but I want to be sure. Anyone know if he is right?
(Responses will not affect my judgement on this candidate, I've already submitted my decision to my manager.)
Upvotes: 77
Views: 49997
Reputation: 1
#define NULL (void *)0
printf("NULL %d\n", NULL);
results == 0
#define NULL (void *)1
printf("NULL %d\n", NULL);
results == 1
From the above, when we say NULL. We are telling the compiler that memory of NULL is to be treated as null pointer meaning, which is nothing.
Value of zero for NULL has its own perks in terms of conditions in if else or while loops or any other condition.
Otherwise we can assign our own NULL pointers in our programs.
Upvotes: -1
Reputation: 140569
In C, there is one, and only one, context where it is necessary to explicitly cast a null pointer constant to a specific pointer type in order for the program to operate correctly. That context is passing a null pointer through an untyped function argument list. In modern C, this only happens when you need to pass a null pointer to a function that takes a variable number of arguments. (In legacy C, it happens with any function not declared with a prototype.) The paradigmatic example is execl
, where the very last argument must be a null pointer explicitly cast to (char *)
:
execl("/bin/ls", "ls", "-l", (char *)0); // correct
execl("/bin/ls", "ls", "-l", (char *)NULL); // correct, but unnecessarily verbose
execl("/bin/ls", "ls", "-l", 0); // undefined behavior
execl("/bin/ls", "ls", "-l", NULL); // ALSO undefined behavior
Yes, that last example has undefined behavior even if NULL
is defined as ((void *)0)
, because void *
and char *
are not implicitly interconvertible when passed through an untyped argument list, even though they are everywhere else. (There is language in C2011 that makes them implicitly interconvertible when passed through va_arg
, but they forgot to specify that implementation-provided library functions access variadic arguments as-if by calling va_arg
, so you can only rely on that for variadic functions that are part of your program. Someone should probably file a DR.)
"Under the hood", the problem here is not just with the bit pattern used for a null pointer, but that the compiler may need to know the exact concrete type of each argument in order to set up a call frame correctly. (Consider the MC68000, with its separate address and data registers; some ABIs specified pointer arguments to be passed in address registers but integer arguments in data registers. Consider also any ABI where int
and void *
are not the same size. And it's vanishingly rare nowadays, but C does still explicitly provide for [EDIT: I'm not sure, but this may no longer be permitted.]) If there's a function prototype, the compiler can use that, but unprototyped functions and variadic arguments offer no such assistance.void *
and char *
not being the same size.
C++ is more complicated and I don't feel qualified to explain how.
Upvotes: 11
Reputation: 7630
On some implementations, size of pointer is not the same as the size of integer. NULL in integer context is 0, but the actual binary layout does not have to be all 0s.
Upvotes: 2
Reputation: 272497
I'm assuming you mean the null pointer. It is guaranteed to compare equal to 0
.1 But it doesn't have to be represented with all-zero bits.2
See also the comp.lang.c FAQ on null pointers.
Upvotes: 77
Reputation: 123458
The null pointer constant is always 0. The NULL
macro may be defined by the implementation as a naked 0
, or a cast expression like (void *) 0
, or some other zero-valued integer expression (hence the "implementation defined" language in the standard).
The null pointer value may be something other than 0. When a null pointer constant is encountered, it will be converted to the proper null pointer value.
Upvotes: 16
Reputation: 15969
§ 6.3.2.3 of the C99 standard says
An integer constant expression with the value 0, or such an expression cast to type void *, is called a null pointer constant) If a null pointer constant is converted to a pointer type, the resulting pointer, called a null pointer, is guaranteed to compare unequal to a pointer to any object or function.
§ 7.17 also says
[...] NULL which expands to an implementation-defined null pointer constant [...]
The address of the NULL pointer might be different from 0, while it will behave like it was in most cases.
(This should be the same as in older C standards, which I don't have at hand right now)
Upvotes: 17