2011-06-12:

Why NULL points to 0?

c:null:security:hacking
A few years ago I would answer the above question with "because NULL is defined as a void pointer to 0", which is only half correct (and close to being wrong). The answer to this question is much more complicated and thus much more interesting.

Let's start with checking what the C standards (or actually drafts of the standards) say about the (in)famous NULL ptr. The green/yellow/orange colors mark the part that caught my attention. I'll leave the C++0x case for another time (C++0x introduces the nullptr of std::nullptr_t type btw). For TL;DR scroll down, I summarize the points anyway.

C89 aka ANSI C
3.2.2.3 Pointers
[...]
  An integral 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 assigned to or compared for equality to a
pointer, the constant is converted to a pointer of that type.  Such a
pointer, called a null pointer, is guaranteed to compare unequal to a
pointer to any object or function.


  Two null pointers, converted through possibly different sequences
of casts to pointer types, shall compare equal.

C1x (draft N1548)
6.3.2.3 Pointers
[...]
3  An integer constant expression with the value 0, or such an expression cast to type
  void *, is called a null pointer constant.66)
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.


4  Conversion of a null pointer to another pointer type yields a null pointer of that type.
  Any two null pointers shall compare equal.

5  An integer may be converted to any pointer type. Except as previously specified, the
  result is implementation-defined, might not be correctly aligned, might not point to an
  entity of the referenced type, and might be a trap representation.67)

66) The macro NULL is defined in (and other headers) as a null pointer constant; see 7.19.
67) The mapping functions for converting a pointer to an integer or an integer to a pointer are intended to
be consistent with the addressing structure of the execution environment.


6.5.3.2 Address and indirection operators
[...]
4 The unary * operator denotes indirection. If the operand points to a function, the result is
a function designator; if it points to an object, the result is an lvalue designating the
object. If the operand has type ‘‘pointer to type’’, the result has type ‘‘type’’. If an
invalid value has been assigned to the pointer, the behavior of the unary * operator is
undefined.102)

102) [...]
Among the invalid values for dereferencing a pointer by the unary * operator are a null pointer, an
address inappropriately aligned for the type of object pointed to, and the address of an object after the
end of its lifetime.


6.5.9 Equality operators
[...]
6  Two pointers compare equal if and only if both are null pointers, both are pointers to the
  same object (including a pointer to an object and a subobject at its beginning) or function,
  both are pointers to one past the last element of the same array object, or one is a pointer
  to one past the end of one array object and the other is a pointer to the start of a different
  array object that happens to immediately follow the first array object in the address
  space.109)


7.19 Common definitions <stddef.h>
[...]
3  The macros are
    NULL
  which expands to an implementation-defined null pointer constant;


UPDATE: I've corrected some mistakes in the post, since, as Dab pointed out (thanks!), the (int*)0 formula also returns a NULL ptr.

So, to summarize the above (also TL;DR):
1. (The yellow markings) A null pointer is either an integer expression equal to 0 or 0 cast to (void*) type. Important: please note, that it does not say that the null pointer is equal to address 0. It just describes how to get a null pointer constant (cast 0 to void* or just use integer 0 which will be implicit cast during comparison anyway) - treat this as a special case of casting that creates a new magical being called the null pointer.
2. The null pointer constant is defined in stddef.h using the name NULL.
3. If you want a null pointer of other type then void* or integer, you need to cast the null pointer constant to that given type, e.g. (int*)NULL. Important: this explicitly says that you need to use the null pointer constant and not some other value, like e.g. a pointer pointing to address 0.
4. (The orange markings) You can convert an integer into any pointer type (except integer 0 which, as said before, is a special case) and this is consistent with the addressing structure used on a given execution environment. Important: this says that e.g. (int*)0x1234 will point to the address 0x1234, but (int*)0 will not point at address 0, but become a null pointer constant of type int* instead, because it's a null pointer constant (integer 0) cast to type int*. To get a int* pointing at address 0 you would have to do e.g. ((int*)(sizeof(int))) - 1 which, due to pointer arithmetic, would become an int* pointing at address 0.
5. (The green markings) Comparing the null pointer with any legit pointer results in getting a 'false'. I expect this might be rephrased as comparing the null pointer with any non-null pointer will result in getting a 'false'.

The summary: the standard does not say that "NULL is defined as a void pointer to 0", it only says that you get a null ptr constant by using integer 0 or casting 0 to void*, which then gets converted to some totally different being that does not point to address 0.

Going further, we an interesting case:

if( (int*)0 == (((int*)(sizeof(int)) - 1) ) { YES } else { NO }
According to the above points, the answers should be: NO.
That's because the left side is: a null pointer constant (integer 0) cast to type int* - so a null pointer of type int*.
The right side on the other hand is: a pointer of type int* pointing at address 4 (for 32-bit ints) that gets one sizeof(int) worth of bytes subtracted (i.e. 4 bytes in this case) - so it's a pointer to address 0 of type int*.
And, due to the green rule, this should be compare as 'unequal' and result in the 'else' block being executed.

Of course, as one might suspect, executing the code compiled by GCC/MSVC on Windows/Linux-based-OSes resulted in getting a YES. Due to implementation/architectural/historical reasons (at least I suspect so) there is no stored meta-data about a pointer being a null pointer. The implementation just assumes that if a pointer points to address 0 then it's a null pointer (which results to incorrect behavior according to the standard, though is acceptable in practical terms).

OK, some arguments against my way of thinking (aka discussing with myself) and some ways to correct this "issue":

→ "You said that address 0 may contain a legit object, which is not true because you cannot allocate memory at address 0."
Actually this is platform dependent and e.g. both on Windows and Linux you can allocate memory at address 0 - on Windows using the NtAllocateVirtualMemory function and on Linux (afair) using mmap (in both cases with non-zero address that belongs to the first page of the memory). Furthermore, all null-ptr dereference into local priv. escal. exploits abuse this. It's also used (if I remember correctly) by NTVDM. So, you can allocate memory at address zero and you can place some data there, thus you can create a legit pointer that points to address 0.

→ "But ``null'' means ``zero'', so NULL has to point at 0"
It also means "nothing" according to my dictionary ;)

→ "It's hard to store the null pointer as something else on Windows/Linux-based-OS"
Actually I somewhat agree with this one, especially in a 32-bit execution environment.
For example one might say that on Windows in user mode using the value 0x80000000 is better since there is no way to allocate that memory (by default user memory ends at 0x7fffffff).
However, one can allocate this memory if he runs windows with the /3G flag (or similar) and uses the LARGE ADDRESS AWARE flag for the PE - then the process can allocate memory up to 0xC0000000 (exclusive) on a 32-bit system.
→ "So why not use the 0xC0000000 as the null pointer?"
Because when running the same app using WoW64 (Windows on Windows 64) you can use the full 32-bit address space, meaning you can allocate memory at any address. So... no options left.

By the way...
On 22nd Nov'24 we're running a webinar called "CVEs of SSH" – it's free, but requires sign up: https://hexarcana.ch/workshops/cves-of-ssh (Dan from HexArcana is the speaker).


→ "Hmm, but what about e.g. 0xFFFFFFFF? Reading more then a byte from such address would result in an exception being generated, right?"
Not exactly. I couldn't find the confirmation for this in the Intel Manuals (though I would bet it is somewhere there), but e.g. BOCHS emulator has the following interesting line of code in the file paging.cc:

if (! long64_mode()) laddr2 &= 0xffffffff; /* handle linear address wrap in legacy mode */

So reading e.g. a DWORD from address FFFFFFFF would cause a read from page FFFFF and page 0 (which, as said, can be allocated).
But actually... that's totally not important since:
* You can store a valid char at address FFFFFFFF (so there a legit pointer could have this value)
* The standard explicitly says that dereferencing a null pointer is undefined. It does not have to cause a crash. Furthermore a crash here is not required and should not be taken as a prerequisite for selecting a null pointer value/implementation.

→ "What about 64-bits? Any hope there?"
The case is a little different. For example for Windows 64-bit execution environments on x86, only 44-bits of the 64-bit pointer value are used as an offset into memory (that's OK since the address bus is 48-bit anyway afair). Furthermore, the valid values for the top 20 bits are either all ones or all zeroes.
So, why not use the other 20-bits to contain some invalid value (e.g. 1010 1010 1010 1010 1010 binary) that is guaranteed to be an invalid address by the system architecture? Well.. what about a future case when e.g. on 64-bit systems you will be able to address the whole 64-bit range? It happened with 32-bit /3G on WoW64 after all for the 32-bit range.
But yet, that's one solution.

→ "What about storing a flag ``is a null pointer'' in additional meta-data?"
That's totally doable. The only problem is, that you would have to use 5 bytes to store a 4 byte pointer: 4 byte memory offset + 1 bit padded to a byte for the flag. Oh, wait, actually you would have to pad that to 8 bytes (memory access optimization for array of pointers). So... you would use up 2 times more memory for storing pointers, and thanks to that, the aforementioned comparison of (int*)0 == (void*)0 would return false, as it should! Of course, comparison would be two times slower, since the code would have to check the flags first and the offsets second.
Of course, you could try optimizing the memory usage to e.g. fit all the flags in some array, which would allow not wasting 3 and 7/8 bytes per pointer.
As for dereferengins of such a pointer constructed in such a way, it's important to note you would not have to do checks on dereferencing it since dereferencing a pointer is undefined (i.e. the compiler can assume you never try to do it, and hence not include code that checks if you actually do it). Though it might actually be worth doing for security reasons (it would slow the code down greatly though).

→ "What about agreeing on an address that could never be allocated?"
That's a good solution (and even used on some systems that really disallow allocating the 0 page). Also, I don't think anyone on the current desktop platforms would miss 4KB of memory. It could even be the memory at address 0 to maintain the compatibility with the current apps.
Please note, that reserving the memory is a solution not because dereferencing a pointer that points there would cause an exception, but because it's a way to make sure no object will be stored there. As said before, we don't care what would happen if someone dereferences a NULL pointer (it's undefined)... well OK, we care if we care for security.

→ "But... is this part of the C standard reaaally as important to actually implement any of this?"
Yes. No.
Yes: As mentioned before a common bug in kernel mode code is a NULL pointer dereference and redefining NULL as something non-existing in user-mode memory would save the day. Unfortunately, as mentioned before, it is really hard to choose a right value / right implementation. I would vote for disallowing allocation of the 0th page. But... this isn't exactly a compiler vs standard issue, since the standard says explicitly not to care what happens on dereference.
No: Ignoring the security issue, the uncommon comparison of (int*)0 with NULL giving false is not rationale enough to actually implement any of this imho. It would slow things down greatly, cause compatibility issues, break ABI, etc. But it might be something worth thinking about if we ever design a new platform.

To summarize everything - just don't assume that (void*)0 aka NULL always points to 0 or always does something (e.g. causes an exception) on deference. The first one is implementation specific and the second one is totally undefined.

And that's that.

P.S. One of my pet theories is that playing Magic: The Gathering greatly increases the ability to read through standards & lawyer-style documents (that doesn't mean the above deduction is correct; if you think it's not, please leave a comment ;>).

Comments:

2011-06-12 20:17:11 = Kele
{
Great job, as usual Gyn! I'm waiting for the post about C++0x :)

Btw: do you know how NULL or something like that is defined in other languages? Maybe it's not just a C/C++ specific problem.
}
2011-06-12 20:29:15 = Dab
{
About "Going further, we have some interesting cases:":

"Conversion of a null pointer to another pointer type yields a null pointer of that type. Any two null pointers shall compare equal." "An integer constant expression with the value 0, or such an expression cast to type void *, is called a null pointer constant"

IMO it means that integer "0" is always a null pointer, no matter how many casts happened.

So: 0 is a null pointer, (void*)0 is a null pointer, but also (int*)0 and (int*)(void*)0 are null pointers.

Also, about legit object at address 0:

"Such a pointer, called a null pointer, is guaranteed to compare unequal to a pointer to any object or function."

My guess is that C never create "objects" at memory address 0. It would require to read specs to find what "object" is, but you can't do stack/heap/static allocation of such object. Stack/static allocation is directly maintained by compiler and heap allocation is guaranteed not to return 0 because such return value has special meaning, according to CRT.

If you allocate something at 0 using low-level OS trickery it's still just an address, not a real object. And specs doesn't say anything about comparing null pointer to addresses :)
}
2011-06-12 21:21:52 = anon
{
Nice reading as always

Just a question. It still posible mmap page 0?? I think since 2.6.3X it cant be posible.
}
2011-06-12 21:44:12 = Gynvael Coldwind
{
@Kele
Thx ;)
As for your question - I don't know, but I expect this to be a compiled-languages only problem, thus it would be worth checking in Turbo Pascal and ADA. I'll do that later (unless someone does that (be)for(e) me) ;>

@Dab
Ad "IMO it means that integer "0" is always a null pointer, no matter how many casts happened."
I disagree. That means exactly as written, i.e. to form a null pointer you need to cast 0 to void*. Then you can cast the null pointer to anything, and it will remain a null pointer.
But casting e.g. pointer to address 0 of type int* to void* does not form a new pointer.
So 'no matter how many casts happened' is somewhat correct if you take the order of the casts into account.
So, (int*)(void*)0 is a null ptr indeed, but (void*)(int*)0 is a pointer to 0 of type void*.

Ad "So: 0 is a null pointer"
No, 0 is an integer ;) Unless it's cast to a pointer (e.g. by an implicit cast). If you cast it to void*, it's a null pointer, otherwise it's a pointer to address 0.

Ad "(void*)0 is a null pointer"
Yes.

Ad "but also (int*)0 and (int*)(void*)0 are null pointers."
No. The first one is a pointer to address 0 of type int*, and the second one is a null pointer of type int*.

This is because the first case is handled by:
- point 5: "An integer may be converted to any pointer type"
- and footnote 67) being: "The mapping functions for converting a pointer to an integer or an integer to a pointer are intended to be consistent with the addressing structure of the execution environment".
Hence the output in a pointer to address 0.

And the second case is handled by:
- point 3 (first cast counting by order-of-execution): "An integer constant expression with the value 0, or such an expression cast to type void *, is called a null pointer constant."
- and point 4: "Conversion of a null pointer to another pointer type yields a null pointer of that type."
Hence the output is a null pointer.

Ad "My guess is that C never create "objects" at memory address 0. "
Actually memory _address_ management is beyond both C and it's standard. That's something the operating system handles or is implementation defined, and I didn't see any information about it in the description of the standard memory management functions (malloc & co.).
Nevertheless, I agree that it's unlikely for address 0 to be ever allocated on OSes like Windows or Linux-based, due to that value being used as NULL (which, as said in the post, might not be a good choice). But again, this behavior is implementation defined and not C-specific. The standard says only that in some cases a null pointer is returned.

However, note 289) for calloc function in the draft is interesting:
"The space is initialized to all bits zero.289)"
"289) Note that this need not be the same as the representation of floating-point zero or a null pointer constant."
So it proves otherwise - it's noted in the standard that a null pointer constant might or might not be same as zero represented binary.

Ad "Stack/static allocation is directly maintained by compiler"
To be precise, allocation _on the stack_ is maintained by the compiler, but stack allocation itself is something the OS does.

Ad "If you allocate something at 0 using low-level OS trickery it's still just an address, not a real object."
While I agree that allocating at 0 in Windows/Linux is trickery...
What's a "real" object? What makes e.g. a malloc-allocated memory contain "real" object? Actually I don't recall the need to mark in dynamic memory real objects, etc. If you derefer something as an int, then it's an int as far as the compiler is concerned. There is no division for "real" objects and "unreal" objects (surely such would have to exist if "real" objects would exist).
Hence, derefering e.g. an int from address 0 is enough for that int to be an object. And if everything else is valid (memory being allocated, access rights, etc), then this is nothing more then an object at address 0. Which is (should be according to the standard) different then a null pointer ;)
}
2011-06-12 22:50:31 = Dab
{
"There is no division for "real" objects and "unreal" objects (surely such would have to exist if "real" objects would exist)."

Uhm, that's surely not the best naming, but for "real" objects I meant objects managed by the compiler. So, in C case I meant just stack/static objects.

"An integral constant expression with the value 0, or such an expression cast to type void * , is called a null pointer constant."

IMO spec says that "0" and "(void*)0" are basically the same thing -- null pointer constant.

"If a null pointer constant is assigned to or compared for equality to a pointer, the constant is converted to a pointer of that type. "

And only after compare/assign to another pointer, null pointer constant becomes null pointer. So,

extern int * q;

q == 0 // 0 is a null pointer
q == (void*)0 // (void*)0 is a null pointer
q == (int*)0 // if (void*)(int*)0 is a null pointer, then (int*)0 is a null pointer as well, because spec doesn't recognize between 0 and (void*) as null pointer constant

At least this is how all compilers think :) Otherwise, how could null pointer be preserved during function calls? I can think of following code:

void test_null(int * ptr, void * ptr2)
{
if (ptr == ptr2) { ... }
}
extern int * q;
test_null(q, 0);
test_null(q, (void*)0);
test_null(q, (int*)0);

If compilers would use "your" null definition then it would surely lead to severe security issues ;) Because if null pointer is only defined as constant integer, then how can the fact of being null pointer (constant) be preserved during function call? BTW, how is unary ! operator defined for pointers? They surely must be compared to something.

BTW: I believe that pointer "0" is much more fun in segmented mode :) (and so are some weird architectures that have different pointers for data and functions)
}
2011-06-12 23:35:52 = przemoc
{
Damn, it's pretty late, so tl;dr for today, but you can check out also your co-worker's (in a broad sense) posts:

http://mina86.com/2010/10/24/0-is-ambiguous/
http://mina86.com/2011/03/27/null-the-never-ending-story/

Possibly nothing new there, but you never know...

// Posts regarding NULL seem almost like conditio sine qua non of tech- prefix (at least occasional) legalization in blogs. How boring. :)
}
2011-06-13 00:28:41 = Gynvael Coldwind
{
@przemoc
Thanks for the links. I didn't realize mina86 has a blog, huh.

Ehehe, yeah, I guess repeating the same thing over and over again is in fact boring. Nevertheless, this post also servers as a data dump for me for later use (so it is allowed to be boring ;>).

@Dab
Ad "Uhm, that's surely not the best naming, but for "real" objects I meant objects managed by the compiler. So, in C case I meant just stack/static objects. "
Ah OK. Nevertheless I would not limit "real objects" to only static/stack ones.

Ad "An integral constant expression with the value 0..."
OK I see my error - I've misinterpreted part of the sentence in the standard. I'll get a good night sleep and fix the post a little - thanks for pointing this out.
So, the proper pointer pointing to address 0 but not being a null pointer would be e.g.:
char *ptr = ((char*)1) - 1;
As far as my understanding goes, char* 1 is a pointer to address 1. Subtracting 1 should not make it a null pointer, because no integer 0 is present in sight. So it should result in being a non-null pointer.

Ad "Otherwise, how could null pointer be preserved during function calls?"
That's exactly my point - it's not preserved. In current definition of NULL (i.e. address 0) the function does not know if it has been given a function a null pointer or pointer to address 0 because it's not distinguishable due to the specifics of the chosen definition.
The post contains a few ideas how this could be achieved, be it with additional bit for storing this information, or an invalid address in the 64-bit mode (though I'm not saying it's worth implementing/using them... but it would do wonders for security).

Ad "If compilers would use "your" null definition then it would surely lead to severe security issues ;) Because if null pointer is only defined as constant integer, then how can the fact of being null pointer (constant) be preserved during function call? "
I think you are confusing my ideas from the post with the current implementation used e.g. on Windows & Linux based OSes, which is NULL being equal to all bits zeroed of a pointer variable (which is a constant integer, which, as said, does not allow to distinguish between address zero and a null pointer, which again, as said, leads to security problems).
My ideas (though I don't claim they are unique) are the ones at the bottom of the post. And while I agree that the meta-data one does not solve the security problems (but it allows some flexibility), the 64-bit one does (for now).

Ad "BTW, how is unary ! operator defined for pointers?"
I'll check tomorrow, but I'm almost sure it's sth like "returns true for a null ptr and false for anything else", so the comparison is vs a null ptr (which on Windows/Linux would be unfortunately the address to 0).

Ad "segmented mode"
Yeah. I wonder how was that done hehehe. Need to check later.
}
2011-06-13 07:21:44 = http
{
I think a pointer, by design on 32bit systems, is four bytes long. So having an additional flag wouldn't work. You cannot store 4+1-byte pointers on a 32bit system.

I think it's common understanding that any such notation is a NULL pointer - even if the docs contradict. In this case I would simply think the specification doc is "wrong", not the implementation.

As another interesting point: What do you think compatibility-wise, if any compiler would change this behaviour?
}
2011-06-13 08:18:26 = Gynvael Coldwind
{
@http
Ad "You cannot store 4+1-byte pointers on a 32bit system."
Why not? I don't see any reason you cannot do a 8 byte structure, 4 bytes offset into memory that would be used on dereferencing, and 1 bit padded as a flag that would be used on comparison, call it a "pointer" and implement specific code generation on the compiler side. It's actually quite easy to do though, as said, it would introduce additional latency e.g. while transfering such a structure and on pointer comparing.
I think you mixed up an abstract thing called 'the pointer' (which can be any length and contain any fields, as long as the compiler understands it and it's compatible with ABI) with the memory offset used in machine code (which indeed is limited to 32-bit, or 32-bit with a segment selector).
}
2011-06-13 08:49:23 = Gynvael Coldwind
{
@Dab
OK, post fixed.
}
2011-06-13 13:08:02 = Dab
{
Nice. In fact, according to spec I must agree with your example :) But still I think that the spec is just somewhat overgeneralised, because taking NP constant definition as literally as you did would prevent null pointer checks in functions, like

void foo(int *a) { if (a == 0) return; ...

About saving null pointer information: I don't think that any special value besides 0 makes sense, even in 64 bit mode. OK, 16 exabytes is hell of a memory, but so was 4 gigabytes at a time :) So what you would really need is storing pointer type along with the pointer itself. You could store not just null pointer flag but also the type of an object. Therefore, during pointer comparison you could check not only addresses but also types (like strict aliasing). And voila, we have C# references ;) But IMO this is just very contrary to C design goals like 1:1 relation between primitive types and hardware types. So, in practical terms, I'm cool with null == 0 ;)
}
2011-06-29 22:14:25 = mathrick
{
Just as a point of reference, NULL does *not* point to 0 on all existing systems. Namely, Lisp Machines had an architecture not in any way based on C's ideas, and onto which C compilers have been ported afterwards. Thus, in ZetaC, the NULL ptr has a bit pattern very different from 0, because it actually points to a special NULL object. This, like many "theoretically possible but in practice no-one does it that way" things has caused some breakages in badly written C code which assumed NULL to be identical to integer 0, although probably not as many as the earlier VAX BSD-specific "NULL ignores writes and gives 0 on reads" assumption.

Some more reading: http://lists.tunes.org/archives/lispos/1997-June/001659.html
}
2011-06-30 07:49:37 = Gynvael Coldwind
{
@Dab
Ad your example - it would not prevent such checks since there is an implicit cast in the condition:
if (a == (int*)0) ...
And as you pointed out earlier, (int*)0 is a NULL pointer.

@mathrick
Thanks, that's very interesting input :)
}
2013-07-17 17:05:06 = mina86
{
I see przemoc has already advertised my blog so I don't have to, :P but nonetheless, a few notes:

> if( (int*)0 == (((int*)(sizeof(int)) - 1) ) { YES } else { NO }

Note that this is undefined behaviour. First of all “(int *)4” is not guaranteed to point to any object. Second of all, even if pointer “p” pointed to a valid object, “p - 1” would not be guaranteed to be defined (note lack of symmetry here as “p + 1” *is* always guaranteed to be a valid pointer if *p is a valid object).

> Actually this is platform dependent and e.g. both on Windows and Linux you can allocate memory at address 0 - on Windows using the NtAllocateVirtualMemory function and on Linux (afair) using mmap (in both cases with non-zero address that belongs to the first page of the memory)

On Linux, low addresses are explicitly prohibited from being allocated. This is done in round_hint_to_min()[1] function (and if you don't believe me you can trace the call from the mmap syscall[2]). The default amount of protected memory can be set at compile time as well as at run time:

$ cat /proc/sys/vm/mmap_min_addr
65536

[1] http://lxr.linux.no/linux+*/mm/mmap.c#L1186
[2] http://lxr.linux.no/linux+*/mm/mmap.c#L1352

Besides, mmap() would interpret NULL pointer as “put my memory wherever” so you would not be able to allocate virtual page zero that way anyway (and as far as I understand WinAPI the same happens there).

comp.lang.c FAQ lists some machines with null pointer representation different then all zero bits[3] and there's nothing fundamentally stopping anyone from defining null pointer on x86 to be some arbitrary non-zero number, but if that was the case, many things would be more complicated (starting from the fact that this address must be guaranteed to never be valid).

[3] http://c-faq.com/null/machexamp.html
}

Add a comment:

Nick:
URL (optional):
Math captcha: 4 ∗ 6 + 5 =