I am interested in the definedness (undefinedness, implementation-definedness) of casts from pointers to integers and various related operations. Mostly I am interested in C11, but answers for other standard versions (or even C++) are welcome.
For the purposes of this question, assume that the C implementation provides intptr_t
.
Consider the following functions:
#include <assert.h>
#include <stdint.h>
int x;
int y;
int z[2];
void f1(void) {
int *p = &x;
intptr_t i = p;
}
void f2(void) {
int *p = &x;
intptr_t i1 = p;
intptr_t i2 = p;
assert(i1 == i2);
}
void f3(void) {
int *p1 = &x;
int *p2 = &y;
intptr_t i1 = p1;
intptr_t i2 = p2;
assert(i1 != i2);
}
void f4(void) {
int *p1 = &x;
intptr_t i1 = p1;
int *p2 = i1;
intptr_t i2 = p2;
assert(i1 == i2);
}
void f5(void) {
int *p1 = &z[0];
int *p2 = &z[1];
intptr_t i1 = p1;
intptr_t i2 = p2;
assert(i1 < i2);
}
void*
instead of int*
? How about any other data type as the target of the pointer?int*
to intptr_t
and back? (Asking since GCC warns about the casts.)assert
s are guaranteed never to trigger?This is what the C11 standard has to say about intptr_t
:
7.20.1.4 Integer types capable of holding object pointers
The following type designates a signed integer type with the property that any valid pointer to
void
can be converted to this type, then converted back to pointer tovoid
, and the result will compare equal to the original pointer:intptr_t
And the same for uintptr_t
(other than signed -> unsigned).
Also from "6.5.4p3 Cast operators":
Conversions that involve pointers, other than where permitted by the constraints of 6.5.16.1, shall be specified by means of an explicit cast.
Where 6.5.16.1 doesn't mention assigning pointers to an integer type, and vice versa (other than a 0
constant). That means that you do need a cast when assigning, gcc just allows it as a compiler extension (And it doesn't compile at all with -pedantic-errors
)
As for the exact value that is returned in these conversions, this is what the standard has to say:
6.3.2.3 Pointers
p5 An integer may be converted to any pointer type. Except as previously specified, the result is implementation-defined, [...]
p6 Any pointer type may be converted to an integer type. Except as previously specified, the result is implementation-defined. [...]
The basic guarantee you have is:
int x;
(int*) (void*) (intptr_t) (void*) &x == &x;
/* But the void* casts can be implicit */
(int*) (intptr_t) &x == &x;
And it's not necessary to cast to the same integer. E.g., the following can be true:
int x;
(intptr_t) &x != (intptr_t) &x;
Adding casts where necessary, and turning your asserts into returns (since assert(false)
is undefined behaviour), none of your functions have undefined behaviour, but f2
, f4
and f5
can be false. f3
must be true, since the two integers must be different to convert to different pointers.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With