sizeof (int) == sizeof (void*)?
Solution 1
According to this Wikipedia page, in C99 your stdint.h header might declare intptr_t and uintptr_t, but then that of course requires
- C99
- A compiler implementor which has chosen to implement this optional part of the standard
So in general I think this one is tough.
Solution 2
Simply put, no. Not guaranteed on all architectures.
My question is: why? If you want to allocate a type big enough to store a void*
, the best thing to allocate is (surprisingly enough :-) a void*
. Why is there a need to fit it within an int
?
EDIT: Based on your comments to your duplicate question, you want to store special values of the pointer (1,2,3) to indicate extra information.
NO!! Don't do this!!. There is no guarantee that 1, 2 and 3 aren't perfectly valid pointers. That may be the case in systems where you're required to align pointers on 4-byte boundaries but, since you asked about all architectures, I'm assuming you have portability as a high value.
Find another way to do it that's correct. For example, use the union (syntax from memory, may be wrong):
typedef struct {
int isPointer;
union {
int intVal;
void *ptrVal;
}
} myType;
Then you can use the isPointer 'boolean' to decide if you should treat the union as an integer or pointer.
EDIT:
If execution speed is of prime importance, then the typedef solution is the way to go. Basically, you'll have to define the integer you want for each platform you want to run on. You can do this with conditional compilation. I would also add in a runtime check to ensure you've compiled for each platform correctly thus (I'm defining it in the source but you would pass that as a compiler flag, like "cc -DPTRINT_INT
"):
#include <stdio.h>
#define PTRINT_SHORT
#ifdef PTRINT_SHORT
typedef short ptrint;
#endif
#ifdef PTRINT_INT
typedef int ptrint;
#endif
#ifdef PTRINT_LONG
typedef long ptrint;
#endif
#ifdef PTRINT_LONGLONG
typedef long long ptrint;
#endif
int main(void) {
if (sizeof(ptrint) != sizeof(void*)) {
printf ("ERROR: ptrint doesn't match void* for this platform.\n");
printf (" sizeof(void* ) = %d\n", sizeof(void*));
printf (" sizeof(ptrint ) = %d\n", sizeof(ptrint));
printf (" =================\n");
printf (" sizeof(void* ) = %d\n", sizeof(void*));
printf (" sizeof(short ) = %d\n", sizeof(short));
printf (" sizeof(int ) = %d\n", sizeof(int));
printf (" sizeof(long ) = %d\n", sizeof(long));
printf (" sizeof(long long) = %d\n", sizeof(long long));
return 1;
}
/* rest of your code here */
return 0;
}
On my system (Ubuntu 8.04, 32-bit), I get:
ERROR: ptrint typedef doesn't match void* for this platform.
sizeof(void* ) = 4
sizeof(ptrint ) = 2
=================
sizeof(short ) = 2
sizeof(int ) = 4
sizeof(long ) = 4
sizeof(long long) = 8
In that case, I'd know I needed to compile with PTRINT_INT (or long). There may be a way of catching this at compile time with #if, but I couldn't be bothered researching it at the moment. If you strike a platform where there's no integer type sufficient for holding a pointer, you're out of luck.
Keep in mind that using special pointer values (1,2,3) to represent integers may also not work on all platforms - this may actually be valid memory addresses for pointers.
Still ,if you're going to ignore my advice, there's not much I can do to stop you. It's your code after all :-). One possibility is to check all your return values from malloc and, if you get 1, 2 or 3, just malloc again (i.e., have a mymalloc() which does this automatically). This'll be a minor memory leak but it'll guarantee no clashes between your special pointers and real pointers.
Solution 3
The C99 standard defines standard int types:
7.18.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 to void, and the result will compare equal to the original pointer:
intptr_t
The following type designates an unsigned integer type with the property that any valid pointer to void can be converted to this type, then converted back to pointer to void, and the result will compare equal to the original pointer:
uintptr_t
These types are optional.
C99 also defines size_t and ptrdiff_t:
The types are
ptrdiff_t
which is the signed integer type of the result of subtracting two pointers;
size_t
which is the unsigned integer type of the result of the sizeof operator; and
The architectures I've seen have the maximum size of an object equal to the whole memory, so sizeof(size_t) == sizeof(void*), but I'm not aware of anything that is both portable to C89 ( which size_t
is ) and guaranteed to be large enough ( which uintptr_t
is ).
Solution 4
This would be true on a standard 32 bit system, but there certainly are no guarantees, and you could find lots of architectures where it isn't true. For example, a common misconception is that sizeof(int) on x86_64 would be 8 (since it's a 64 bit system, I guess), which it isn't. On x86_64, sizeof(int) is still 4, but sizeof(void*) is 8.
Solution 5
The standard solution to this problem is to write a small program which checks the sizes of all int types (short int, int, long int) and compares them to void*. If there is a match, it emits a piece of code which defines the intptr type. You can put this in a header file and use the new type.
It's simple to include this code in the build process (using make
, for example)
![Admin](/assets/logo_square_200-5d0d61d6853298bd2a4fe063103715b4daf2819fc21225efa21dfb93e61952ea.png)
Admin
Updated on June 09, 2022Comments
-
Admin about 2 years
Is there an integer type with the same size as pointer? Guaranteed on all microarchitectures?
-
mrivard over 15 yearsBut note that
int
in general is 32 bits on x86-64, so in some cases you might rather want to uselong
. -
jcopenha over 15 yearsThough you do have to be careful with alignment issues here. For example, if void* is 64-bits and int is 32-bits, which 32-bits of the void* are matched with the int?
-
unwind over 15 yearsOf course, there is no guarantee here that you can access all the bits of the pointer member through the integer member. That is hopefully obvious, but I thought I'd point it out anyway.
-
Christoph over 15 yearsGCC implements it; for Visual Studio, try azillionmonkeys.com/qed/pstdint.h or code.google.com/p/msinttypes
-
Christoph over 15 yearsThere's the MinGW version as well: cygwin.com/cgi-bin/cvsweb.cgi/src/winsup/mingw/include/…
-
Admin over 15 yearsThe problem here is that you have to check the memory/cacheline twice in the union soltion (for isPointer and ptrVal). This is the innermost loop of the cycle - nanoseconds make a real difference.
-
Admin over 15 yearsOk, understood. I cannot have both portability and micro-optimisation. Still, is anyone aware of a platform where malloc can actually return values of 1, 2 or 3?
-
paxdiablo over 15 yearsSee updates to answwer, Martin. If you really want to avoid clashes, code your own mymalloc() which doesn't pass through those addresses. It's a kludge, but it should work.
-
Admin over 15 yearsPax, thanks for the answer. However, let me suggest a different way to solve the problem: unsigned char *dummy = malloc (4); void *special_value1 = dummy + 0; .... This would keep the special values in L1d cache rather than in L1i, but I think I can do with that.
-
Friedrich over 15 yearsThat's right it just requires sizeof(short) <= sizeof(int) <= sizeof(long), but the guarantees are there if one uses the stdint.h provided limits
-
Jonathan Leffler over 15 yearsptrdiff_t and size_t are also in C89, unlike intptr_t and uintptr_t.
-
Jonathan Leffler over 15 yearsUsing int on a 64-bit machine would be wrong except in the exceptional case of ILP64. DEC Alpha was the leading example of ILP64; I'm not aware of any current architectures using ILP64 rather than LP64 or (for Windows 64) LLP64.
-
Pete Kirkham over 15 yearsI've added an aside to indicate what types fail each arm of the 'both portable to C89 and guaranteed to be large enough'
-
tc. about 12 yearsThe standard does NOT guarantee that they are the same size; only that you can cast to (
u
)intptr_t
and back again (provided the memory it references is still valid), and that a null pointer gets converted to 0. On "sensible" architectures it will just be a memory address, but this is by no means guaranteed (e.g. the cast could randomly permute the bits, there's no guarantee that you can infer alignment from theuintptr_t
representation, pointer arithmetic doesn't need to work...). -
Erik Ekman over 11 yearsChecks like this should preferably be compile-time asserts instead of having to be executed to detect a problem.
-
paxdiablo over 11 yearsErik, the runtime checks are just an added protection against the developer entering the wrong type during compilation and only needed until your makefile (or other build tool) is configured correctly - I tend to wrap the whole thing in an
#ifdef DEVMODE
macro so it doesn't affect production code. -
Deduplicator almost 4 years@tc. Since when does the C standard guarantee that a null pointer converts to a zero integer?
-
tc. about 3 yearsHuh, indeed it does not. C89/99 only seems to guarantee that converting an "integer constant expression with the value 0" to a pointer gives a null pointer, so e.g.
int x = 0; if ((void*)x) { ... }
is implementation-defined. It would be really weird if behaviour changed drastically the moment the compiler decided something was no longer constant, though.