C has a type uintptr_t
which lets you store a pointer value:
void *p = …;
uintptr_t u = (uintptr_t)p;
void *q = (void *)q;
// here q is equivalent to p
On many platforms, u
stores the address of the object that p
point to, just like p
does, and u
and p
have the same representation in memory. This is not an obligation: C implementations are allowed to use a different representation. But using the same representation is the most obvious way to meet the requirement in the C standard.
Note that a conversion from uintptr_t
to a pointer type only gives a usable pointer if the uintptr_t
value was obtained from a pointer that is still valid. This can be subtle. Even on processor architectures where the representation is the same (which is almost all of them), compilers are allowed to assume that you don't do too crazy things and to optimize accordingly. See responses to the Cerberus survey for some examples.
In your case, you didn't try to store the pointer value in an uintptr_t
, you tried to store it in an int
. This failed because on your platform, int
is too small to store a pointer value. Evidently, on your platform, pointers are 64-bit values and int
are 32-bit values. This is a very common arrangement.
a
is the address of the array. (It's a pointer to the first element of the array, and on your platform a pointer is just represented by an address.). In this particular run, it's 0x7ffee8d30600. Nothing remarkable there.
(int) a
converts the address of a
to an int
value. This is implementation-defined. Your implementation does the usual thing: since int
is too small, it truncates the value. Truncating the address above to an unsigned 32-bit value yields 0xe8d30600
. To get a signed value, the most significant bit becomes the sign bit, so a
happens to be negative: a
is -388823552
.
p
is (int *) ((int) a)
. This conversion is again implementation-defined, and your implementation again does the same thing as most. It takes the 32-bit signed int
value -388823552
, extends it to the desired width and shifts it to the unsigned range, yielding 264 - 388823552 = 0xffffffffe8d30600.
(int)p
applies the same truncation process as (int)a
above, and again yields -388823552
. But you're using the %x
specifier to print it out, so this value is converted to unsigned
(a 32-bit type on your platform), which gives the value 232 - 388823552 = 0xe8d30600.