views:

3547

answers:

11

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

+2  A: 

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.

Liedman
+12  A: 

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.

unwind
good point about the typedefs.
Rob McCready
GCC implements it; for Visual Studio, try http://www.azillionmonkeys.com/qed/pstdint.h or http://code.google.com/p/msinttypes/
Christoph
Christoph
A: 

No.

I do not believe the C standard even specifies standard int sizes. Combine that with all the architectures out there (8/16/32/64bit etc) and there is no way to guarantee anything.

Rob McCready
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
Friedrich
A: 

int data type would be the answer on most architectures.

But thre is NO guarantee to this for ANY (micro)architecture.

AlexDrenea
But note that `int` in general is 32 bits on x86-64, so in some cases you might rather want to use `long`.
Magnus Hoff
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.
Jonathan Leffler
A: 

The answer seems to be "no", but if all you need is a type that can act as both, you can use a union:

union int_ptr_t {
    int i;
    void* p;
};
Magnus Hoff
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?
Zooba
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.
unwind
Nope. I want to store pointer in an integer.
A: 

No, the closest you will come to a portable pointer-capable integer type would be intptr_t and ptrdiff_t.

Ronny Vindenes
+8  A: 

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.

paxdiablo
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.
You *can* store all the necessary information in a pointer field, but *only* if you throw out the portability-concern. Special case your code if you really need this.
Magnus Hoff
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?
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.
paxdiablo
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.
Oops. Even better: char dummy [4]; void *special_value = ...
+7  A: 

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 ).

Pete Kirkham
ptrdiff_t and size_t are also in C89, unlike intptr_t and uintptr_t.
Jonathan Leffler
I've added an aside to indicate what types fail each arm of the 'both portable to C89 and guaranteed to be large enough'
Pete Kirkham
+2  A: 

So what about C++. Do I have to go to the lengths of:

template <int BITS> struct int_traits_t;
template <> struct int_traits_t <16> {typedef uint16_t int_t;};
template <> struct int_traits_t <32> {typedef uint32_t int_t;};
template <> struct int_traits_t <64> {typedef uint64_t int_t;};
typedef int_traits_t <sizeof (void*) * 8>::int_t myintptr_t;

kust to get portable interger of the size of pointer?

If you indent by four spaces, your code will be formatted as code.
Magnus Hoff
A: 

Usually sizeof(*void) depends on memory bus width (although not necessarily - pre-RISC AS/400 had 48-bit address bus but 64-bit pointers), and int usually is as big as CPU's general-purpose register (there are also exceptions - SGI C used 32-bit ints on 64-bit MIPS).

So there is no guarantee.

qrdl
+2  A: 

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)

Aaron Digulla