31

Is it a good idea to use intptr_t as a general-purpose storage (to hold pointers and integer values) instead of void*? (As seen here: http://www.crystalspace3d.org/docs/online/manual/Api1_005f0-64_002dBit-Portability-Changes.html)

For what I've already read:

  • int -> void* -> int roundtrip is not guaranteed to hold original value; I guess int -> intptr_t -> int will do
  • pointer arithmetics on both void* and intptr_t require casts, so none gets advantage here
  • void* means less explicit casts when storing pointers, intptr_t means less casts when storing integer values
  • intptr_t requires C99

What else should I take into consideration?

RzR
  • 3,068
  • 29
  • 26
Gepard
  • 499
  • 1
  • 4
  • 7
  • 1
    No. (If it was, then they'd just have added intptr_t's semantics to `void*`) – Billy ONeal Feb 29 '12 at 02:34
  • The post asks "(to hold pointers and integer values)", yet then only discusses `int`, `void *` and `intptr_t`. As `uintmax_t`, `size_t`, `long long`, etc. are also integer types, sounds like the question is only about object pointers, `intptr_t` and `int` types. – chux - Reinstate Monica Jan 28 '16 at 23:11

2 Answers2

34

Is it a good idea to use intptr_t as a general-purpose storage (to hold pointers and integer values) instead of void*?

No.

intptr_t is not guaranteed to exist. First, as you note, it was introduced in C99. Second, implementations are not required to have an integer type big enough to hold converted pointer values without loss of information.

Converting an int to intptr_t and back is unlikely to lose information but there's no actual guarantee that intptr_t is wider than int.

If you want to store pointer values, store them in pointer objects. That's what pointer objects are for.

Any pointer to an object or incomplete type can be converted to void* and back again without loss of information. There is no such guarantee for pointers to functions -- but any pointer-to-function type can be converted to any other pointer-to-function-type and back without loss of information. (I'm referring to the C standard; I think POSIX provides some additional guarantees.)

If you want to store either an integer or a pointer value in the same object, the first thing you should do is re-think your design. If you've already done so, and concluded that you really do want to do this, consider using a union (and keeping careful track of what kind of value you've stored most recently).

There are APIs that use a void* argument to allow arbitrary data to be passed; see, for example, the POSIX pthread_create() function. This can be abused by casting an integer value to void* but it's safer to pass the address of an integer object.

Keith Thompson
  • 254,901
  • 44
  • 429
  • 631
  • 2
    Please expand on "_unlikely_ to lose information". `intptr_t` does round-trip a pointer to one the compares _equally_ - is that potentially not-the-same-bit pattern the loss of info of your comment. – chux - Reinstate Monica Jan 28 '16 at 20:55
  • 3
    @chux: As I said, there's no guarantee that `intptr_t` is wider than `int` (though I know of no implementation where it isn't). If, for example, `intptr_t` is 32 bits and `int` is 64 bits, then converting an `int` to `intptr_t` and back to `int` could lose information. Specifically, if the `int` value is outside the range of `intptr_t`, the first conversion yields an implementation-defined result. – Keith Thompson Jan 28 '16 at 22:54
  • 2
    Sorry, read the answer incorrectly as "Converting a _pointer_ to `intptr_t` and back is unlikely to lose info...". My comment was based on that thought. Agree about the rarity of the `int/intptr_t` issues. – chux - Reinstate Monica Jan 28 '16 at 23:04
7

No, you can't be guaranteed that any particular type is a reasonable way of storing both pointers and integers, and besides, it makes your code confusing. There is a better way.

If you want to store an integer and a pointer in the same object, the clean and portable method is to use a union:

union foo {
   int integer_foo;
   void *pointer_foo;
};

This is portable and allows you to store both sorts of things in the size of storage needed for the larger of the two. It is guaranteed to always work.

Sean Bright
  • 118,630
  • 17
  • 138
  • 146
Perry
  • 4,363
  • 1
  • 17
  • 20
  • 2
    Do `int` and `void*` have a common initial subsequence in terms of layout? Otherwise this is _not_ portable, you are _not_ allowed to store both members at once and this is absolutely _not_ guaranteed to "always work". – Lightness Races in Orbit Feb 08 '14 at 17:39
  • 3
    @LightnessRacesinOrbit I don't think he was claiming that two values could be stored in it at once . "both" means that an `int` may be stored in it, and a `void *` may be stored in it, but there is no suggestion of simultaneity. NB. It wouldn't matter if `int` and `void*` had common initial sequence as that rule only applies to structs. – M.M Jan 28 '16 at 20:34
  • 1
    @M.M: I think "both" does imply simultaneity in a way that "either" would not have done, but who knows. Certainly, the OP is looking for simultaneity given the talk of roundtrip conversions in the question, so if this answer is not wrong then it is at least posted on the wrong question :P – Lightness Races in Orbit Jan 28 '16 at 22:03