Reputation: 107
From my understanding, according to C standard, casting an int pointer to int is unadvised for portable code. A simple example would be doing such a cast on a 64-bit architecture where pointers are 64 bits but integer types are 32 bits, in this case a cast would truncate information, ie an actual physical example of how things can go wrong
The same is true for casting an integer to an int pointer. However, I cannot find an example as to why exactly this is considered to be UB/implementation specific. I get that C standard advises against it, but what exactly can go wrong? The only vague example I found was somebody mentioning possible alignment issues, how exactly would those arise?
Upvotes: 1
Views: 199
Reputation: 214060
The C standard is fairly detailed in listing possible problems, C17 6.3.2.3/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.
So the various potential problems are:
Different sizes. This is the most obvious issue. A pointer address might not fit inside an integer, or the other way around.
Alignment. An integer containing some random number might cause a misaligned address when converted to a pointer.
Incorrect addresses, such as misaligned ones or addresses pointing into executable code rather than data, may cause implementation defined "traps"/hardware exceptions.
Far less likely, integers can in theory contain trap representations too, but that's likely only relevant for exotic/fictional one's complement and signed magnitude systems. The C standard allows for such systems, but very few such systems have actually existed in the history of computers.
Wrong type. If we lie to the compiler when converting to/from pointers and tell it that there's another type stored at a location than what's actually stored there, we might get into all manner of problems. We may screw up the compiler's internal track of what types that are stored where, so called "strict pointer aliasing violations". This in turn might cause optimization-related bugs.
What is the strict aliasing rule?
We may also, once again, cause problems with misalignment and traps, or just by the program not making any sense of what's stored at a certain location.
Pointer arithemtic on unknown addresses. There may be issues with casting to a physical address where the compiler doesn't know what's stored (no known type) and then perform pointer arithmetic from there. Because pointer arithmetic is only well-defined when pointing at an array of a known type. Strictly speaking, doing so is undefined behavior, so it might cause some poor compiler implementations to bug out and produce random behaving code. Hosted system compilers are known to do this - it's a quality of implementation problem. In particular, be very afraid of such bugs when using the gcc compiler for embedded systems programming.
Exotic pointer formats. Some systems utilize extended addressing modes, that go beyond the default address bus width. This is very common in low-end embedded systems with 8-/16 bit addresses, but also existed in the PC world back in the MS DOS days. Typically such extended addresses are using a non-standard pointer type (a common non-standard extension is the far
keyword). Converting to/from these pointers types and integers will be very system-specific.
The most correct type to use for converting to/from pointer types is uintptr_t
. This is defined to be "large enough" and suitable to hold the representation of a pointer.
On some exotic systems we may also use intptr_t
which is the signed equivalent. That one only makes sense if the OS has some weird internal virtual addressing, such as placing kernel space at negative addresses.
Upvotes: 2
Reputation: 5936
Not all platforms have the same size int
and int*
. This can lead to truncation and alignment problems among others. It can also seemingly work without problems.
For portable behavior, it is advisable to use the fixed-width integers defined by the C99 standard in <stdint.h>
.
You would use an uintptr_t
as the variable to hold a pointer-address.
See this answer as well: When is casting between pointer types not undefined behavior in C?
Upvotes: 1