views:

1351

answers:

19

In C (or C++ for that matter), pointers are special if they have the value zero: I am adviced to set pointers to zero after freeing their memory, because it means freeing the pointer again isn't dangerous; when I call malloc it returns a pointer with the value zero if it can't get me memory; I use if (p != 0) all the time to make sure passed pointers are valid etc.

But since memory addressing starts at 0, isn't 0 just as a valid address as any other? How can 0 be used for handling null pointers if that is the case? Why isn't a negative number null instead?


Edit:

A bunch of good answers. I'll summarize what has been said in the answers expressed as my own mind interprets it and hope that the community will correct me if I misunderstand.

  • Like everything else in programming it's an abstraction. Just a constant, not really related to the address 0. C++0x emphasizes this by adding the keyword nullptr.

  • It's not even an address abstraction, it's the constant specified by the C standard and the compiler can translate it to some other number as long as it makes sure it never equals a "real" address, and equals other null pointers if 0 is not the best value to use for the platform.

  • In case it's not an abstraction, which was the case in the early days, the address 0 is used by the system and off limits to the programmer.

  • My negative number suggestion was a little wild brainstorming, I admit. Using a signed integer for addresses is a little wasteful if it means that apart from the null pointer (-1 or whatever) the value space is split evenly between positive integers that make valid addresses and negative numbers that are just wasted.

  • If any number is always representable by a datatype, it's 0. (Probably 1 is too, I think of the one-bit integer which would be 0 or 1 if unsigned, or just the signed bit if signed, or the two bit integer which would be [-2, 1]. But then you could just go for 0 being null and 1 being the only accessible byte in memory.)

Still there is something that is unresolved in my mind. This question http://stackoverflow.com/questions/2389251/pointer-to-a-specific-fixed-address tells me that even if 0 for null pointer is an abstraction, other pointer values aren't necessarily. This leads me to post another question: http://stackoverflow.com/questions/2761360/could-i-ever-want-to-access-the-address-zero

A: 

Perhaps because 0 works for unsigned numbers as well as signed.

Justin Ethier
+9  A: 

IIRC, the "null pointer" value isn't guaranteed to be zero. The compiler translates 0 into whatever "null" value is appropriate for the system (which in practice is probably always zero, but not necessarily). The same translation is applied whenever you compare a pointer against zero. Because you can only compare pointers against each other and against this special-value-0, it insulates the programmer from knowing anything about the memory representation of the system. As for why they chose 0 instead of 42 or somesuch, I'm going to guess it's because most programmers start counting at 0 :) (Also, on most systems 0 is the first memory address and they wanted it to be convenient, since in practice translations like I'm describing rarely actually take place; the language just allows for them).

rmeador
@Justin: You misunderstood. The constant 0 is *always* the null pointer. What @meador is saying is that it's possible that the null pointer (indicated by the constant 0) does not correspond to the address zero. On some platforms, creating a null pointer (`int* p = 0`) might create a pointer containing the value `0xdeadbeef` or any other value it prefers. 0 is a null pointer, but a null pointer is not necessarily a pointer to address zero. :)
jalf
A NULL pointer is a reserved value and depending on the compiler could be any bit pattern. NULL pointer does not mean that it points to address 0.
Shaji
But @Jalf, the constant 0 *isn't* always the null pointer. It's what we write when we want the compiler to fill in the platform's *actual* null pointer for us. Practically speaking, the null pointer usually *does* correspond to the address zero, though, and I interpret Joel's question as asking why that is. There's supposedly a valid byte of memory at that address, after all, so why not use a non-existent address of a non-existent byte instead of removing a valid byte from play? (I'm writing what I imagine Joel was thinking, not a question I'm asking myself.)
Rob Kennedy
@Rob: Sort of. I know what you mean, and you're correct, but so am I. :)The constant integer 0 represents the null pointer at a source code level. Comparing a null pointer to 0 yields true. Assigning 0 to a pointer sets that pointer to null. 0 *is* the null pointer.But the actual in-memory representation of a null pointer might be different from the zero bit pattern. (Anyway, my comment was in response to @Justin's now deleted comment, not to @Joel's question. :)
jalf
@jalf @Rob You need some terms to clarify, I think. :) From §4.10/1: "A *null pointer constant* is an integral constant expression rvalue of integer type that evaluates to zero. A null pointer constant can be converted to a pointer type; the result is the *null pointer value* of that type and is distinguishable from every other value of pointer to object or pointer to function type."
GMan
+12  A: 

Historically, the address space starting at 0 was always ROM, used for some operating system or low level interrupt handling routines, nowadays, since everything is virtual (including address space), the operating system can map any allocation to any address, so it can specifically NOT allocate anything at address 0.

Aviad P.
That pretty much is it. It is by historical convention, and the first addreses were used for interrupt handlers, thus are unusable for normal programs. Also, 0 is "empty", which can be interpreted as no value / no pointer.
TomTom
+24  A: 

2 points:

  • only the constant value 0 in the source code is the null pointer - the compiler implementation can use whatever value it wants or needs in the running code. Some platforms have a special pointer value that's 'invalid' that the implementation might use as the null pointer. The C FAQ has a question, "Seriously, have any actual machines really used nonzero null pointers, or different representations for pointers to different types?", that points out several platforms that used this property of 0 being the null pointer in C source while represented differently at runtime. The C++ standard has a note that makes clear that converting "an integral constant expression with value zero always yields a null pointer, but converting other expressions that happen to have value zero need not yield a null pointer".

  • a negative value might be just as usable by the platform as an address - the C standard simply had to chose something to use to indicate a null pointer, and zero was chosen. I'm honestly not sure if other sentinel values were considered.

The only requirements for a null pointer are:

  • it's guaranteed to compare unequal to a pointer to an actual object
  • any two null pointers will compare equal (C++ refines this such that this only needs to hold for pointers to the same type)
Michael Burr
+1 I suspect 0 was chosen merely for historical reasons. (0 being a starting and invalid address, most of the time.) Of course in general such an assumption isn't always true, but 0 works pretty well.
GMan
Space may also have been a contributing factor. In the days when C was first developed, memory was MUCH more costly than now. The number zero can be conveniently calculated using an XOR instruction or without the need to load an immediate value. Depending upon the architecture, this could potentially save space.
Sparky
@GMan - You are correct. On early CPUs, memory address zero was special and had hardware protection against access from running software (in some cases it was the start of the reset vector, and modifying it could prevent the CPU from resetting or starting up). Programmers used this hardware protection as a form of error detection in their software, letting the CPU's address decode logic check for uninitialized or invalid pointers instead of having to spend CPU instructions to do it. The convention remains to this day, even though the purpose of address zero may have changed.
bta
Minix 16 bit compiler used 0xFFFF for NULL.
Joshua
In many embedded systems, 0 is a valid address. The value -1 (all bits one) is also a valid address. Checksums for ROMs are hard to calculate when the data starts at address 0. :-(
Thomas Matthews
Accepted on the combined merits of having the core of the most answers and vote count.
Joel
In some machines testing for 0 is quicker than testing for other values. (Testing for the zero flag after an operation) This could have been a contributing factor also.
daramarak
+2  A: 

I think it's just a convention. There must be some value to mark an invalid pointer.

You just lose one byte of address space, that should rarely be a problem.

There are no negative pointers. Pointers are always unsigned. Also if they could be negative your convention would mean that you lose half the address space.

Axel Gneiting
A: 

In one of the old DEC machines (PDP-8, I think), the C runtime would memory protect the first page of memory so that any attempt to access memory in that block would cause an exception to be raised.

Paul Tomblin
+1  A: 

Historically the low memory of an application was occupied by system resources. It was in those days that zero became the default null value.

While this is not necessarily true for modern systems, it is still a bad idea to set pointer values to anything but what memory allocation has handed you.

Fred Haslam
+4  A: 

But since memory addressing starts at 0, isn't 0 just as a valid address as any other?

On some/many/all operating systems, memory address 0 is special in some way. For example, it's often mapped to invalid/non-existent memory, which causes an exception if you try to access it.

Why isn't a negative number null instead?

I think that pointer values are typically treated as unsigned numbers: otherwise for example a 32-bit pointer would only be able to address 2 GB of memory, instead of 4 GB.

ChrisW
+1  A: 

Although C uses 0 to represent the null pointer, do keep in mind that the value of the pointer itself may not be a zero. However, most programmers will only ever use systems where the null pointer is, in fact, 0.

But why zero? Well, it's one address that every system shares. And oftentimes the low addresses are reserved for operating system purposes thus the value works well as being off-limits to application programs. Accidental assignment of an integer value to a pointer is as likely to end up zero as anything else.

George Phillips
A: 

Rarely does an OS allow you to write to address 0. It's common to stick OS-specific stuff down in low memory; namely, IDTs, page tables, etc. (The tables have to be in RAM, and it's easier to stick them at the bottom than to try and determine where the top of RAM is.) And no OS in its right mind will let you edit system tables willy-nilly.

This may not have been on K&R's minds when they made C, but it (along with the fact that 0==null is pretty easy to remember) makes 0 a popular choice.

cHao
This isn't true in protected mode, and in fact, on certain Linux configurations, you _can_ write to virtual address 0.
Longpoke
+1  A: 

The choice of sentinel value is arbitrary, and this is in fact being addressed by the next version of C++ (informally known as "C++0x", most likely to be known in the future as ISO C++ 2011) with the introduction of the keyword nullptr to represent a null valued pointer. In C++, a value of 0 may be used as an initializing expression for any POD and for any object with a default constructor, and it has the special meaning of assigning the sentinel value in the case of a pointer initialization. As for why a negative value was not chosen, addresses usually range from 0 to 2N-1 for some value N. In other words, addresses are usually treated as unsigned values. If the maximum value were used as the sentinel value, then it would have to vary from system to system depending on the size of memory whereas 0 is always a representable address. It is also used for historical reasons, as memory address 0 was typically unusable in programs, and nowadays most OSs have parts of the kernel loaded into the lower page(s) of memory, and such pages are typically protected in such a way that if touched (dereferenced) by a program (save the kernel) will cause a fault.

Michael Aaron Safyan
A: 

The value 0 is a special value that takes on various meanings in specific expressions. In the case of pointers, as has been pointed out many many times, it is used probably because at the time it was the most convenient way of saying "insert the default sentinel value here." As a constant expression, it does not have the same meaning as bitwise zero (i.e., all bits set to zero) in the context of a pointer expression. In C++, there are several types that do not have a bitwise zero representation of NULL such as pointer member and pointer to member function.

Thankfully, C++0x has a new keyword for "expression that means a known invalid pointer that does not also map to bitwise zero for integral expressions": nullptr. Although there are a few systems that you can target with C++ that allow dereferencing of address 0 without barfing, so programmer beware.

MSN
A: 

Go ahead and try it.

JohnMcG
What? Try what?
GMan
+2  A: 

You must be misunderstanding the meaning of constant zero in pointer context.

Neither in C nor in C++ pointers can "have value zero". Pointers are not arithmetic objects. They canot have numerical values like "zero" or "negative" or anything of that nature. So your statement about "pointers ... have the value zero" simply makes no sense.

In C/C++ pointers can have the reserved null-pointer value. The actual representation of null-pointer value has nothing to do with any "zeros". It can be absolutely anything appropriate for a given platform. It is true that on most plaforms null-pointer value is represented physically by an actual zero address value. However, if on some platform address 0 is actually used for some purpose (i.e. you might need to create objects at address 0), the null-pointer value on such platform will most likely be different. It could be physically represented as 0xFFFFFFFF address value or as 0xBAADBAAD address value, for example.

Nevertheless, regardless of how the null-pointer value is respresented on a given platform, in your code you will still continue to designate null-pointers by constant 0. In order to assign a null-pointer value to a given pointer, you will continue to use expressions like p = 0. It is the compiler's responsibility to realize what you want and translate it into the proper null-pointer value representation, i.e. to translate it into the code that will put the address value of 0xFFFFFFFF into the pointer p, for example.

In short, the fact that you use 0 in your sorce code to generate null-pointer values does not mean that the null-pointer value is somehow tied to address 0. The 0 that you use in your source code is just "syntactic sugar" that has absolutely no relation to the actual physical address the null-pointer value is "pointing" to.

AndreyT
<quote>Pointers are not arithmetic objects</quote> Pointer arithmetic is quite well defined in C and C++. Part of the requirement is that both pointers point within the same composite. The null pointer doesn't point to any composite so using it in pointer arithmetic expressions is illegal. For example, it is not guaranteed that `(p1 - nullptr) - (p2 - nullptr) == (p1 - p2)`.
Ben Voigt
@Ben Voigt: The language specification defines the notion of *arithmetic type*. All I'm saying is that pointer types do not belong to the category of arithmetic types. *Pointer arithmetic* is a different and completely unrelated story, a mere linguistic coincidence.
AndreyT
How is someone reading *arithmetic objects* supposed to know it means "in the sense of arithmetic types" and not "in the sense of arithmetic operators" (several of which are usable on pointers) or "in the sense of pointer arithmetic". As far as linguistic coincidences are concerned, *arithmetic object* has more letters in common with *pointer arithmetic* than *arithmetic types*. At the same time, the standard does talk about *pointer value*. The original poster probably meant *integer representation of a pointer* rather than *pointer value*, and `NULL` explicitly need not be represented by 0.
Ben Voigt
Well, for example the term *scalar objects* in C/C++ terminology is just a shorthand for *objects of scalar types* (just like *POD objects* = *objects of POD types*). I used the term *arithmetic objects* in exactly the same way, meaning *objects of arithmetic types*. I expect "someone" to understand it that way. Someone who doesn't can always ask for a clarification.
AndreyT
A: 

There are historic reasons for this, but there are also optimization reasons for it.

It is common for the OS to provide a process with memory pages initialized to 0. If a program wants to interpret part of that memory page as a pointer then it is 0, so it is easy enough for the program to determine that that pointer is not initialized. (this doesn't work so well when applied to uninitialized flash pages)

Another reason is that on many many processors it is very very easy to test a value's equivalence to 0. It is sometimes a free comparison done without any extra instructions needed, and usually can be done without needing to provide a zero value in another register or as a literal in the instruction stream to compare to.

The cheap comparisons for most processors are the signed less than 0, and equal to 0. (signed greater than 0 and not equal to 0 are implied by both of these)

Since 1 value out of all of possible values needs to be reserved as bad or uninitialized then you might as well make it the one that has the cheapest test for equivalence to the bad value. This is also true for '\0' terminated character strings.

If you were to try to use greater or less than 0 for this purpose then you would end up chopping your range of addresses in half.

nategoose
A: 

My guess would be that the magic value 0 was picked to define an invalid pointer since it could be tested for with less instructions. Some machine languages automatically would set the zero and sign bits when loading registers so you could test for a null pointer with a simple load then and branch instructions without doing a load then compare and then branch.

On the Commodore Pet, Vic20, and C64 which were the first machines I worked on, RAM started at location 0 so it was totally valid to read and write using a null pointer if you really wanted to.

KPexEA
A: 

The constant 0 is used instead of NULL because C was made by some cavemen trillions of years ago, NULL, NIL, ZIP, or NADDA would have all made much more sense than 0.

But since memory addressing starts at 0, isn't 0 just as a valid address as any other?

Indeed. Although a lot of operating systems disallow you from mapping anything at address zero, even in a virtual address space (people realized C is an insecure language, and reflecting that null pointer dereference bugs are very common, decided to "fix" them by dissallowing the userspace code to map to page 0; Thus, if you call a callback but the callback pointer is NULL, you wont end up executing some arbitrary code).

How can 0 be used for handling null pointers if that is the case?

Because 0 used in comparison to a pointer will be replaced with some implementation specific value, which is the return value of malloc on a malloc failure.

Why isn't a negative number null instead?

This would be even more confusing.

Longpoke
A: 

It has to have some value. Obviously you don't want to step on values the user might legitimately want to use. I would speculate that since the C runtime provides the BSS segment for zero-initialized data, it makes a certain degree of sense to interpret zero as an un-initialized pointer value.

JustJeff
A: 
Noah Roberts