Pointers in the C language are more abstract than pointers in the hardware, and the compiler may surprise developers that think that pointers in C work in the same way as pointers in the CPU.
A C pointer points into what the standard calls an object, points to the byte following the object, or has the value
A C pointer points into what the standard calls an object, points to the byte following the object, or has the value
NULL. The concept of "object" here is very different from the C++ object — an object in the C standard is a range of bytes allocated as a unit,1 so x and y are objects in
int x; struct foo y[10];and the memory returned by a call to
malloc is an object.
Dangling pointers are dangerous, and may invoke undefined behavior even when they are not dereferenced. The reason is that the value of a pointer becomes indeterminate when the object it points to (or just past) reaches the end of its lifetime. That is, a dangling pointer is treated the same way as an uninitialized pointer, and all use of uninitialized values invokes undefined behavior.
void foo(int *p, int *q) { free(p); if (p == q) // Undefined behavior! bar(); }The pointer
p has indeterminate value after free(p), so the comparison invokes undefined behavior.
Comparing pointers using the relational operators (
Arithmetic on a pointer cannot make it point outside the object (more than on the byte following the object). In particular, arithmetic on a pointer cannot make it point into another object. This is useful for compilers, as they can use this to track memory accesses and trivially determine that reads and writes through pointers derived from different objects do not conflict. Actually, it would be very hard for the compiler to place variables in registers if writes through pointers could modify arbitrary objects!
There is however one special case where a pointer can point to the address of another object — two objects may be placed next to each other in memory, which is typical for cases such as as
GCC has a somewhat aggressive interpretation on the standard, so it compiles
1. The C standard's definition of object is a bit more involved, as it also involves the type of the data within the range of bytes, but this does not affect the discussion in this blog post. I'll come back to the type related part in a future blog post on aliasing.
This blog post was updated 2016-05-19:
<, >, <=, and >=) requires them to point into the same object (or the byte following the object). That is
int *p = malloc(64 * sizeof(int)); int *q = p + i; if (p < q) foo();is fine (provided that 0 ≤
i ≤ 64), but
int *p = malloc(64 * sizeof(int)); int *q = malloc(64 * sizeof(int)); if (p < q) // Undefined behavior! foo();invokes undefined behavior. Similarly, subtraction of pointers are only allowed for array objects, and both pointers must point into the same array object (or the byte following the object).
Arithmetic on a pointer cannot make it point outside the object (more than on the byte following the object). In particular, arithmetic on a pointer cannot make it point into another object. This is useful for compilers, as they can use this to track memory accesses and trivially determine that reads and writes through pointers derived from different objects do not conflict. Actually, it would be very hard for the compiler to place variables in registers if writes through pointers could modify arbitrary objects!
There is however one special case where a pointer can point to the address of another object — two objects may be placed next to each other in memory, which is typical for cases such as as
int x, y;
and p and q can now have the same value after
int *p = &x + 1; int *q = &y;But the pointer
p does not really point to y — it points to the address following x. Dereferencing p invokes undefined behavior, so you cannot really use the fact that p and q have the same value.GCC has a somewhat aggressive interpretation on the standard, so it compiles
p == q to false if it determines that they are derived from different objects (see GCC bug 61502 for details). This has the fun effect that it is possible to get pointers p and q that point at the same memory address, but p == q evaluates to false, as in
#include <stdio.h> int main(void) { int x, y; int *p = &x + 1; int *q = &y; printf("%p %p %d\n", (void*)p, (void*)q, p == q); return 0; }that prints
0x7f7fffffdafc 0x7f7fffffdafc 0on my development machine when compiled with a recent GCC.
1. The C standard's definition of object is a bit more involved, as it also involves the type of the data within the range of bytes, but this does not affect the discussion in this blog post. I'll come back to the type related part in a future blog post on aliasing.
This blog post was updated 2016-05-19:
- Added casts in last example
- Changed 256 to 64*sizeof(int) in examples

