58

The constant 0 is used as the null pointer in C and C++. But as in the question "Pointer to a specific fixed address" there seems to be some possible use of assigning fixed addresses. Is there ever any conceivable need, in any system, for whatever low level task, for accessing the address 0?

If there is, how is that solved with 0 being the null pointer and all?

If not, what makes it certain that there is not such a need?

Community
  • 1
  • 1
Joel
  • 3,227
  • 5
  • 21
  • 16
  • 2
    Interesting question! I suppose because address 0 is just 1 byte that you can live without ... – Hamish Grubijan May 03 '10 at 21:33
  • 3
    Would you consider this a valid use: to cause your program to crash, execute arbitrary code, and take over your system? :) – Paul Williams May 03 '10 at 21:34
  • 6
    some embedded devices store interrupt, etc. at address 0. are you considering embedded devices? – Anycorn May 03 '10 at 21:36
  • @aaa Definitely, all devices where C or other 0-is-null-pointer-language is used is interesting. – Joel May 03 '10 at 21:40
  • @Hamish In C you need considerably more than jut 1 byte to remain "unaddressable" since accessing an array (or structure) that starts at 0 will access bytes above 0. Usually a considerable amount of address space is left unaddressable starting at 0 and going up at least a few KB and usually a few MB. – SoapBox May 04 '10 at 00:38
  • @SoapBox: That's an implementation feature; the language only requires the one byte. – Dennis Zickefoose May 04 '10 at 00:52
  • Remember that only the *constant* integer 0 is converted to a null pointer. A non-constant integer 0 maps to whatever address the implementation chooses (which will typically be address 0 unless the compiler is just designed to annoy ;)) – jalf May 04 '10 at 01:51
  • 3
    As a point of note, both the PlayStation 1 and PlayStation 2 were more than happy to let you grab the value at address 0. Noone did it on purpose since that's where the consoles' kernel lived. However, it made porting to other systems less than fun if some code accidentally depended on not crashing when accessing a "null" pointer. – Jim Buck May 04 '10 at 02:20
  • 1
    @JimBuck thanks for the interesting anecdote! – Marc Claesen Aug 12 '13 at 10:38
  • Note: the effect of dereferencing a null pointer is undefined. A specific implementation is allowed to define it so that dereferencing a null pointer accesses address 0. – user253751 Aug 23 '15 at 02:43
  • This [twitter thread may be useful](https://twitter.com/myrrlyn/status/940365445957279744) – Shafik Yaghmour Dec 14 '17 at 17:56

17 Answers17

83

Neither in C nor in C++ null-pointer value is in any way tied to physical address 0. The fact that you use constant 0 in the source code to set a pointer to null-pointer value is nothing more than just a piece of syntactic sugar. The compiler is required to translate it into the actual physical address used as null-pointer value on the specific platform.

In other words, 0 in the source code has no physical importance whatsoever. It could have been 42 or 13, for example. I.e. the language authors, if they so pleased, could have made it so that you'd have to do p = 42 in order to set the pointer p to null-pointer value. Again, this does not mean that the physical address 42 would have to be reserved for null pointers. The compiler would be required to translate source code p = 42 into machine code that would stuff the actual physical null-pointer value (0x0000 or 0xBAAD) into the pointer p. That's exactly how it is now with constant 0.

Also note, that neither C nor C++ provides a strictly defined feature that would allow you to assign a specific physical address to a pointer. So your question about "how one would assign 0 address to a pointer" formally has no answer. You simply can't assign a specific address to a pointer in C/C++. However, in the realm of implementation-defined features, the explicit integer-to-pointer conversion is intended to have that effect. So, you'd do it as follows

uintptr_t address = 0;
void *p = (void *) address;

Note, that this is not the same as doing

void *p = 0;

The latter always produces the null-pointer value, while the former in general case does not. The former will normally produce a pointer to physical address 0, which might or might not be the null-pointer value on the given platform.

AnT stands with Russia
  • 312,472
  • 42
  • 525
  • 765
  • Wonderful answer. I wish I could upvote this answer **twice** . – Destructor Apr 25 '16 at 14:48
  • Thanks. I have some questions understanding your reply https://stackoverflow.com/questions/52207010/do-the-following-statements-assign-physical-address-to-a-pointer – Tim Sep 06 '18 at 15:10
  • 2
    "You simply can't assign a specific address to a pointer in C/C++." What about `void *p = (void*)0x12345678`? At least on some platforms (in my case, an ARM MCU) this will assign an absolute, physical memory address to a pointer. It is commonly used for SFRs (special function registers, i.e. memory mapped I/O). – jacobq Sep 04 '19 at 21:28
  • @iX3: "in the realm of implementation-defined features" – Tim Sparkles Sep 23 '19 at 18:08
  • @Timbo, whoops, no idea how I missed that; sorry. – jacobq Sep 24 '19 at 13:34
22

On a tangential note: you might be interested to know that with Microsoft's C++ compiler, a NULL pointer to member will be represented as the bit pattern 0xFFFFFFFF on a 32-bit machine. That is:

struct foo
{
      int field;
};

int foo::*pmember = 0;     // 'null' member pointer

pmember will have the bit pattern 'all ones'. This is because you need this value to distinguish it from

int foo::*pmember = &foo::field;

where the bit pattern will indeed by 'all zeroes' -- since we want offset 0 into the structure foo.

Other C++ compilers may choose a different bit pattern for a null pointer to member, but the key observation is that it won't be the all-zeroes bit pattern you might have been expecting.

John Källén
  • 7,551
  • 31
  • 64
  • 2
    Interesting! +1 even if it has little to do with the question :) – Logan Capaldo May 04 '10 at 00:40
  • Wait, what? What happens when i start assigning that pointer to different kind of pointers - with (void *) cast if need be? Will it stay all-1s or what will happen? Does that mean all nulls "are -1" in MS C++? – Nas Banov Mar 13 '14 at 22:51
  • 1
    You're not allowed to cast a member pointer to a "normal" pointer. MS C++ gives "invalid type conversion" errors if you try. Also, only null member pointers have the bit pattern 'all ones' in MS C++; "normal" pointers to objects still have the bit pattern 'all zeros'. – John Källén Mar 16 '14 at 11:00
  • 1
    G++ also does this. – alecov Jan 18 '17 at 14:12
  • Of course, that depends on MS's non-conforming "let's use the simplest member-pointers which might be versatile enough for this specific class". Try going for full-generality, or add virtual inheritance, and you'll see bigger member-pointers. – Deduplicator Jan 15 '19 at 15:57
12

You're starting from a mistaken premise. When you assign an integer constant with the value 0 to a pointer, that becomes a null pointer constant. This does not, however, mean that a null pointer necessarily refers to address 0. Quite the contrary, the C and C++ standards are both very clear that a null pointer may refer to some address other than zero.

What it comes down to is this: you do have to set aside an address that a null pointer would refer to -- but it can be essentially any address you choose. When you convert zero to a pointer, it has to refer to that chosen address -- but that's all that's really required. Just for example, if you decided that converting an integer to a point would mean adding 0x8000 to the integer, then the null pointer to would actually refer to address 0x8000 instead of address 0.

It's also worth noting that dereferencing a null pointer results in undefined behavior. That means you can't do it in portable code, but it does not mean you can't do it at all. When you're writing code for small microcontrollers and such, it's fairly common to include some bits and pieces of code that aren't portable at all. Reading from one address may give you the value from some sensor, while writing to the same address could activate a stepper motor (just for example). The next device (even using exactly the same processor) might be connected up so both of those addresses referred to normal RAM instead.

Even if a null pointer does refer to address 0, that doesn't prevent you from using it to read and/or write whatever happens to be at that address -- it just prevents you from doing so portably -- but that doesn't really matter a whole lot. The only reason address zero would normally be important would be if it was decoded to connect to something other than normal storage, so you probably can't use it entirely portably anyway.

Jerry Coffin
  • 476,176
  • 80
  • 629
  • 1,111
  • 16
    His premise is fine. He wants to know: how do you access memory address 0, since (void *) 0 is special? – Daniel Stutzbach May 03 '10 at 21:56
  • 2
    @Daniel: `(void *) 0` is special. `int x = 0; (void *) x` is not. It certainly isn't portable, but that's not what people who need to access specific memory locations worry about. – David Thornley May 04 '10 at 17:17
  • Where C & C++ standard says that null pointer may refer to some address other than zero? Will you please post relevant citation from the standard that says this? – Destructor Feb 23 '16 at 14:02
  • @PravasiMeet: It's never stated directly, but §4.10 and §4.11 talk about the fact that storing a null pointer constant into a pointer involves a conversion, and are very careful to *not* say that the resulting pointer has all its bits set to 0. – Jerry Coffin Feb 23 '16 at 14:06
  • @JerryCoffin: which kind of conversion happens & why it is required? – Destructor Feb 23 '16 at 14:08
  • @PravasiMeet: The kinds discussed in §4.10 and §4.11 for the reasons discussed in §4.10 and §4.11. – Jerry Coffin Feb 23 '16 at 14:09
  • @JerryCoffin: is it necessary that all bits must be zero for a pointer to be qualified as null pointer? – Destructor Feb 23 '16 at 14:10
  • @PravasiMeet: Only if §4.10 and §4.11 say so. If you want to know the details of what the standard does or doesn't require, you need to read it. I've pointed to the relevant sections repeatedly already. If you read them, and have a specific question, please ask it--but I'm not going to just read it for you. – Jerry Coffin Feb 23 '16 at 14:10
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/104302/discussion-between-pravasi-meet-and-jerry-coffin). – Destructor Feb 23 '16 at 14:11
10

The compiler takes care of this for you (comp.lang.c FAQ):

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.

You can get to address zero by referencing zero from a non-pointer context.

Georg Fritzsche
  • 97,545
  • 26
  • 194
  • 236
WhirlWind
  • 13,974
  • 3
  • 42
  • 42
7

In practice, C compilers will happily let your program attempt to write to address 0. Checking every pointer operation at run time for a NULL pointer would be a tad expensive. On computers, the program will crash because the operating system forbids it. On embedded systems without memory protection, the program will indeed write to address 0 which will often crash the whole system.

The address 0 might be useful on an embedded systems (a general term for a CPU that's not in a computer; they run everything from your stereo to your digital camera). Usually, the systems are designed so that you wouldn't need to write to address 0. In every case I know of, it's some kind of special address. Even if the programmer needs to write to it (e.g., to set up an interrupt table), they would only need to write to it during the initial boot sequence (usually a short bit of assembly language to set up the environment for C).

Daniel Stutzbach
  • 74,198
  • 17
  • 88
  • 77
  • On the ARM architecture, for example, address 0 is the reset vector. If you overwrite what's there (should be a jump to the real reset code) you've broken your ability to soft-reset. (However, address 0 is often mapped to flash memory which can't be overwritten quite so easily.) – crazyscot May 03 '10 at 22:47
  • 1
    RE `In practice, C compilers will happily let your program attempt to write to address 0.` I don't believe this is accurate anymore. Compiler will see naive attempts to write to address 0 and optimize around the UB such as trapping ,see [my answer here for more details](https://stackoverflow.com/a/26310534/1708801) – Shafik Yaghmour Dec 14 '17 at 17:51
6

Memory address 0 is also called the Zero Page. This is populated by the BIOS, and contains information about the hardware running on your system. All modern kernels protect this region of memory. You should never need to access this memory, but if you want to you need to do it from within kernel land, a kernel module will do the trick.

rook
  • 66,304
  • 38
  • 162
  • 239
6

On the x86, address 0 (or rather, 0000:0000) and its vicinity in real mode is the location of the interrupt vector. In the bad old days, you would typically write values to the interrupt vector to install interrupt handers (or if you were more disciplined, used the MS-DOS service 0x25). C compilers for MS-DOS defined a far pointer type which when assigned NULL or 0 would recieve the bit pattern 0000 in its segment part and 0000 in its offset part.

Of course, a misbehaving program that accidentally wrote to a far pointer whose value was 0000:0000 would cause very bad things to happen on the machine, typically locking it up and forcing a reboot.

John Källén
  • 7,551
  • 31
  • 64
  • 3
    Actually, no immediate bad things would be caused by writing to 0 on the x86. 0 is for the interrupt vector of the Divide By Zero exception, and that is rarely triggered.. – Earlz May 03 '10 at 21:48
  • IIRC, on the Motorola 68K series, 0 was the start address - the first instruction to be executed at startup was loaded from address zero. Been a few years, so I may be mistaken, but I've 90% confidence on this although I don't have my references handy to confirm. – Nathan Ernst May 03 '10 at 22:20
  • The 8080 (and therefore the Z80) started execution at power-up at address 0, so your ROM would typically be based there. Wouldn't typically need a pointer to 0000. Attempting a write would, on my TRS-80, have no effect. – John Källén May 03 '10 at 22:24
5

In the question from the link, people are discussing setting to fixed addresses in a microcontroller. When you program a microcontroller everything is at a much lower level there.

You even don't have an OS in terms of desktop/server PC, and you don't have virtual memory and that stuff. So there is it OK and even necessary to access memory at a specific address. On a modern desktop/server PC it is useless and even dangerous.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Andrey
  • 59,039
  • 12
  • 119
  • 163
  • 2
    And in microcontrollers, address 0 is often the program entry point. Resetting the program can therefore be done by jumping to address 0. – Ponkadoodle May 03 '10 at 21:52
3

I compiled some code using gcc for the Motorola HC11, which has no MMU and 0 is a perfectly good address, and was disappointed to find out that to write to address 0, you just write to it. There's no difference between NULL and address 0.

And I can see why. I mean, it's not really possible to define a unique NULL on an architecture where every memory location is potentially valid, so I guess the gcc authors just said 0 was good enough for NULL whether it's a valid address or not.

      char *null = 0;
; Clears 8-bit AR and BR and stores it as a 16-bit pointer on the stack.
; The stack pointer, ironically, is stored at address 0.
1b:   4f              clra
1c:   5f              clrb
1d:   de 00           ldx     *0 <main>
1f:   ed 05           std     5,x

When I compare it with another pointer, the compiler generates a regular comparison. Meaning that it in no way considers char *null = 0 to be a special NULL pointer, and in fact a pointer to address 0 and a "NULL" pointer will be equal.

; addr is a pointer stored at 7,x (offset of 7 from the address in XR) and 
; the "NULL" pointer is at 5,y (offset of 5 from the address in YR).  It doesn't
; treat the so-called NULL pointer as a special pointer, which is not standards
; compliant as far as I know.
37:   de 00           ldx     *0 <main>
39:   ec 07           ldd     7,x
3b:   18 de 00        ldy     *0 <main>
3e:   cd a3 05        cpd     5,y
41:   26 10           bne     53 <.LM7>

So to address the original question, I guess my answer is to check your compiler implementation and find out whether they even bothered to implement a unique-value NULL. If not, you don't have to worry about it. ;)

(Of course this answer is not standard compliant.)

indiv
  • 17,306
  • 6
  • 61
  • 82
  • 0 is a unique value for NULL. It just happens to be a value the system will allow you to write to. On other systems, it is not, so there is a segfault. On other systems, you can write to it, but you probably shouldn't, so some other obscure result occurs [I think the HC11 falls under this category; address 0 is special, but I don't remember what it represents]. All three behaviors are "standard compliant" insofar as the standards leave the behavior undefined. At least, this is the case in C++; I assume C is the same. – Dennis Zickefoose May 04 '10 at 01:14
  • @Dennis Zickefoose: By standards compliant, I was referring to the parts of the standard that mention that a NULL pointer was guaranteed not to point to an object, implying that comparing any other pointer with NULL would return false. On the HC11, address 0 is just regular RAM, nothing special, and you *should* use address 0 for something because you can use direct addressing for RAM from 0 to 255 bytes, resulting in smaller instructions and faster accesses. – indiv May 04 '10 at 14:20
  • 1
    If the compiler is careful never to allocate an object at that address, then it is guaranteed never to point to an object. That's the way it works on other platforms. A null pointer is treated the same as every other pointer, the compiler just knows not to let objects be there. – Dennis Zickefoose May 04 '10 at 16:53
1

It all depends on whether the machine has virtual memory. Systems with it will typically put an unwritable page there, which is probably the behaviour that you are used to. However in systems without it (typically microcontrollers these days, but they used to be far more common) then there's often very interesting things in that area such as an interrupt table. I remember hacking around with those things back in the days of 8-bit systems; fun, and not too big a pain when you had to hard-reset the system and start over. :-)

Donal Fellows
  • 133,037
  • 18
  • 149
  • 215
1

Yes, you might want to access memory address 0x0h. Why you would want to do this is platform-dependent. A processor might use this for a reset vector, such that writing to it causes the CPU to reset. It could also be used for an interrupt vector, as a memory-mapped interface to some hardware resource (program counter, system clock, etc), or it could even be valid as a plain old memory address. There is nothing necessarily magical about memory address zero, it is just one that was historically used for special purposes (reset vectors and the like). C-like languages follow this tradition by using zero as the address for a NULL pointer, but in reality the underlying hardware may or may not see address zero as special.

The need to access address zero usually arises only in low-level details like bootloaders or drivers. In these cases, the compiler can provide options/pragmas to compile a section of code without optimizations (to prevent the zero pointer from being extracted away as a NULL pointer) or inline assembly can be used to access the true address zero.

bta
  • 43,959
  • 6
  • 69
  • 99
1

Remember that in all normal cases, you don't actually see specific addresses. When you allocate memory, the OS supplies you with the address of that chunk of memory.

When you take the reference of a variable, the the variable has already been allocated at an address determined by the system.

So accessing address zero is not really a problem, because when you follow a pointer, you don't care what address it points to, only that it is valid:

int* i = new int(); // suppose this returns a pointer to address zero
*i = 42; // now we're accessing address zero, writing the value 42 to it

So if you need to access address zero, it'll generally work just fine.

The 0 == null thing only really becomes an issue if for some reason you're accessing physical memory directly. Perhaps you're writing an OS kernel or something like that yourself. In that case, you're going to be writing to specific memory addresses (especially those mapped to hardware registers), and so you might conceivably need to write to address zero. But then you're really bypassing C++ and relying on the specifics of your compiler and hardware platform.

Of course, if you need to write to address zero, that is possible. Only the constant 0 represents a null pointer. The non-constant integer value zero will not, if assigned to a pointer, yield a null pointer.

So you could simply do something like this:

int i = 0;
int* zeroaddr = (int*)i;

now zeroaddr will point to address zero(*), but it will not, strictly speaking, be a null pointer, because the zero value was not constant.

(*): that's not entirely true. The C++ standard only guarantees an "implementation-defined mapping" between integers and addresses. It could convert the 0 to address 0x1633de20` or any other address it likes. But the mapping is usually the intuitive and obvious one, where the integer 0 is mapped to the address zero)

jalf
  • 243,077
  • 51
  • 345
  • 550
0

It may surprise many people, but in the core C language there is no such thing as a special null pointer. You are totally free to read and write to address 0 if it's physically possible.

The code below does not even compile, as NULL is not defined:

int main(int argc, char *argv[])
{
    void *p = NULL;
    return 0;
}

OTOH, the code below compiles, and you can read and write address 0, if the hardware/OS allows:

int main(int argc, char *argv[])
{
    int *p = 0;
    *p = 42;
    int x = *p; /* let's assume C99 */
}

Please note, I did not include anything in the above examples. If we start including stuff from the standard C library, NULL becomes magically defined. As far as I remember it comes from string.h.

NULL is still not a core C feature, it's a CONVENTION of many C library functions to indicate the invalidity of pointers. The C library on the given platform will define NULL to a memory location which is not accessible anyway. Let's try it on a Linux PC:

#include <stdio.h>
int main(int argc, char *argv[])
{
        int *p = NULL;
        printf("NULL is address %p\n", p);
        printf("Contents of address NULL is %d\n", *p);
        return 0;
}

The result is:

NULL is address 0x0
Segmentation fault (core dumped)

So our C library defines NULL to address zero, which it turns out is inaccessible. But it was not the C compiler, of not even the C-library function printf() that handled the zero address specially. They all happily tried to work with it normally. It was the OS that detected a segmentation fault, when printf tried to read from address zero.

SzG
  • 12,333
  • 4
  • 28
  • 41
  • 1
    `NULL` must be defined to `0` or `(void *)0`, and it is the part of the compiler (not the library) which performs integer-to-pointer conversion that will convert integer `0` to whatever the compiler wants a null pointer to look like. The C library doesn't make the decision about the null pointer representation. `int *p = 0;` and `int *p = NULL;` are defined to be identical by the C Standard (assuming a header is included that defines `NULL`). – M.M Jan 15 '17 at 00:19
0

C/C++ don't allows you to write to any address. It is the OS that can raise a signal when a user access some forbidden address. C and C++ ensure you that any memory obtained from the heap, will be different of 0.

Vicente Botet Escriba
  • 4,305
  • 1
  • 25
  • 39
0

I have at times used loads from address zero (on a known platform where that would be guaranteed to segfault) to deliberately crash at an informatively named symbol in library code if the user violates some necessary condition and there isn't any good way to throw an exception available to me. "Segfault at someFunction$xWasnt16ByteAligned" is a pretty effective error message to alert someone to what they did wrong and how to fix it. That said, I wouldn't recommend making a habit of that sort of thing.

Stephen Canon
  • 103,815
  • 19
  • 183
  • 269
0

If I remember correctly, in an AVR microcontroller the register file is mapped into an address space of RAM and register R0 is at the address 0x00. It was clearly done in purpose and apparently Atmel thinks there are situations, when it's convenient to access address 0x00 instead of writing R0 explicitly.

In the program memory, at the address 0x0000 there is a reset interrupt vector and again this address is clearly intended to be accessed when programming the chip.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Maciej Hehl
  • 7,895
  • 1
  • 22
  • 23
0

Writing to address zero can be done, but it depends upon several factors such as your OS, target architecture and MMU configuration. In fact, it can be a useful debugging tool (but not always).

For example, a few years ago while working on an embedded system (with few debugging tools available), we had a problem which was resulting in a warm reboot. To help locate the problem, we were debugging using sprintf(NULL, ...); and a 9600 baud serial cable. As I said--few debugging tools available. With our setup, we knew that a warm reboot would not corrupt the first 256 bytes of memory. Thus after the warm reboot we could pause the loader and dump the memory contents to find out what happened prior to reboot.

Sparky
  • 13,505
  • 4
  • 26
  • 27