sizeof (int) == sizeof (void*)?

15,639

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)

Share:
15,639
Admin
Author by

Admin

Updated on June 09, 2022

Comments

  • Admin
    Admin about 2 years

    Is there an integer type with the same size as pointer? Guaranteed on all microarchitectures?

  • mrivard
    mrivard over 15 years
    But note that int in general is 32 bits on x86-64, so in some cases you might rather want to use long.
  • jcopenha
    jcopenha over 15 years
    Though 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
    unwind over 15 years
    Of 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
    Christoph over 15 years
    GCC implements it; for Visual Studio, try azillionmonkeys.com/qed/pstdint.h or code.google.com/p/msinttypes
  • Christoph
    Christoph over 15 years
  • Admin
    Admin over 15 years
    The 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
    Admin over 15 years
    Ok, 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
    paxdiablo over 15 years
    See 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
    Admin over 15 years
    Pax, 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
    Friedrich over 15 years
    That'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
    Jonathan Leffler over 15 years
    ptrdiff_t and size_t are also in C89, unlike intptr_t and uintptr_t.
  • Jonathan Leffler
    Jonathan Leffler over 15 years
    Using 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
    Pete Kirkham over 15 years
    I've added an aside to indicate what types fail each arm of the 'both portable to C89 and guaranteed to be large enough'
  • tc.
    tc. about 12 years
    The 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 the uintptr_t representation, pointer arithmetic doesn't need to work...).
  • Erik Ekman
    Erik Ekman over 11 years
    Checks like this should preferably be compile-time asserts instead of having to be executed to detect a problem.
  • paxdiablo
    paxdiablo over 11 years
    Erik, 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
    Deduplicator almost 4 years
    @tc. Since when does the C standard guarantee that a null pointer converts to a zero integer?
  • tc.
    tc. about 3 years
    Huh, 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.