Mabden said:
<engaging brain, opening mouth, foot at the ready...>
So, you are saying that (on some mythical machine) I could have an
array a[] that lives at location 0000:0000 of memory and point to it
with ptr = &a and ptr would not be a NULL pointer?
Yes (but please use the phrase "null pointer", not "NULL pointer";
NULL is a macro). I'm assuming that your 0000:0000 notation refers to
an all-bits-zero representation.
I'm not sure I can get behind that. If nothing else, the compiler
(or linker or loader, whatever) should disallow storing at memory
location 0.
Why should it?
Despite what K&R2 says on page 102, C says nothing special about an
all-bits-zero address. The only special address defined by the
language is the null pointer, which may or may not be represented as
all-bits-zero. (The wording in K&R2, as far as I can tell, is
consistent with a requirement that null pointers always being
represented as all-bits-zero. The standard itself is not, though of
course a given implementation is allowed to represent null pointers as
all-bits-zero if it chooses).
Also, you are saying that 0 can live anywhere in memory and ptr = 0 (as in
char *ptr = 0) would be NULL. This part I get, and agree with.
<disengaging overheated gears, cooling with beer>
0 is a null pointer constant, as is any integer constant expression
with the value 0, or such an expression cast to void*. Converting a
null pointer constant to any pointer type yields a null pointer, which
is guaranteed to compare unequal to a pointer to any object or
function.
In my opinion, the standard isn't quite as clear as it should be about
things that occur in source vs. things that exist at run time. A
"null pointer constant" is a construct that appears only in C source,
and is interpreted by the compiler. NULL is a macro that expands to a
null pointer constant; again, it appears only in C source. A "null
pointer" is a particular pointer value that can exist when a program
is running (and the source may no longer even exist).
Here's something that might clarify things a bit (or not).
Suppose you're designing a programming language. You need a distinct
pointer value that doesn't point to any object, to be used for things
like indicating that a memory allocation failed, marking the end of a
linked list, etc. Call this value a "nil pointer". You don't want
programmers to care how a "nil pointer" is represented (it might be
0xdeadbeef on one platform, 0xffffffff on another), so you have a
keyword "nil" to represent a nil pointer value:
//
// This is not C.
//
char *ptr = malloc(42);
if (ptr == nil) printf("malloc failed\n");
You can think of the keyword "nil" as a "nil pointer constant".
Now you have a nice clean language. Pointers and integers are two
entirely separate things, and there's no reason anyone should confuse
"nil" (a pointer value) with "zero" (an integer value).
Now you get some feedback from your customers. They complain that
"nil" takes too long to type; they want to use a literal 0 to indicate
a nil pointer value. You give in to their demands. But is "0"
equivalent to "1-1" or "0x0"? It turns out that the cleanest way to
make this work is to say that any integer constant expression with the
value 0, or such an expression cast to void*, is a "nil pointer
constant" (in addition to the existing "nil" keyword).
You release the new language specification, but there are still
complaints. Users don't want "nil" to be a keyword, since they want
to use it as an identifier. So in the next revision of your language,
you remove the "nil" keyword; a "nil pointer constant" is *just* an
integer constant expression with the value 0, or such an expression
cast to void*. You grumble that the users' complaints have messed up
your nice clean design; there'll probably need to be an entire section
in the FAQ just to explain this. And while you're at it, you change
the terminology a bit: "nil pointer" becomes "null pointer", and "nil
pointer constant" becomes "null pointer constant". And you add a NULL
macro that expands to a null pointer constant.
And now your new language is C.
(Incidentally, this is not how C actually evolved. There never was a
"nil" keyword; I'm fairly sure the use of 0 as a null pointer constant
goes back to the beginning, if not farther.)