c – Why do I get cast from pointer to integer of different size error?

c – Why do I get cast from pointer to integer of different size error?

The reason for the warning is that the compiler suspects you might be trying to round-trip a pointer through int and back. This was common practice before the advent of 64-bit machines and it is not safe or reasonable. Of course here the compiler can clearly see that youre not doing this, and it would be nice if it were smart enough to avoid the warning in cases like this, but its not.

A clean alternative that avoids the warning, and another much nastier issue of wrong result when the converted value is negative, is:

unsigned int offset = (uintptr_t) dst % blkLen;

Youll need to include stdint.h or inttypes.h to have uintptr_t available.

The problem is that converting a void* pointer to unsigned int is inherently non-portable.

The possible difference in size is only part of the problem. That part of the problem can be solved by using uintptr_t, a type defined in <stdint.h> and <inttypes.h>. uintptr_t is guaranteed to be wide enough that converting a void* to uintptr_t and back again will yield the original pointer value (or at least a pointer value that compares equal to the original one). Theres also a type intptr_t, which is signed; usually unsigned types make more sense for this kind of thing. uintptr_t and intptr_t are not guaranteed to exist, but they should exist on any (C99 or later) implementation that has the appropriate integer types.

But even if you have an integer type thats big enough to hold a converted pointer, the result isnt necessarily meaningful for anything other than converting back to a pointer.

The C standard says, in a non-normative footnote, that:

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.

which is not helpful unless you happen to know what that addressing structure is.

You seem to be trying to determine what the offset of the void* argument is relative to the next lower multiple of blkLen; in other words, youre trying to determine how the pointer value is aligned with respect to blkLen-sized blocks of memory.

If you happen to know that thats a sensible thing to do on the system youre using, thats fine. But you should be aware that arithmetic operations on integers resulting from pointer conversions are still inherently non-portable.

A concrete example: Ive worked on systems (Cray vector machines) where a void* pointer is a 64-bit machine address (which points to a 64-bit word), with a 3-bit byte offset inserted by software into the otherwise unused high-order 3 bits. Converting a pointer to an integer simply copied the representation. Any integer arithmetic on such an integer is likely to yield meaningless results unless it takes this (admittedly exotic) representation into account.

Conclusions:

  1. You should definitely use uintptr_t rather than playing preprocessor tricks to determine which integer type you can use. The implementer of your compiler has already done the work of determining an integer type that can safely hold a converted pointer value. Theres no need to reinvent that particular wheel. (Caveat: <stdint.h> was added to C by the 1999 ISO standard. If youre stuck using an ancient compiler that doesnt implement it, you might still need to use some kind of #ifdef hacks. But Id still suggest using uintptr_t if its available. You can test __STDC_VERSION__ >= 199901L to test for C99 conformance — though some compilers might support <stdint.h> without fully supporting C99.)

  2. You need to be aware that converting a pointer to an integer and playing with its value is non-portable. Thats not to say you shouldnt do it; one of Cs greatest strengths is its ability to support non-portable code when thats what you need.

c – Why do I get cast from pointer to integer of different size error?

Because casting a void * to an unsigned int is exactly what that warning is intended to catch because it is unsafe. The pointer can be 64-bit and the int can be 32-bit. For any given platform, the sizeof(unsigned int) is not guaranteed to be sizeof(void *). You should use uintptr_t instead.

Leave a Reply

Your email address will not be published.