0

I'm doing some C++ work that requires me to convert "normal" (widely-used) datatypes such as int to their fixed-size counterpoints such as std::int32_t. I have some code that uses void*, however, and this is causing me trouble.

I know the size of any given pointer is the same on a given system - sizeof(void*) == sizeof(int*) == sizeof(short*) == sizeof(long*). But I also know the size of a pointer can vary from system to system (in particular, 32-bit vs 64-bit). Edit for future readers: it's been pointed out that there are no guarantees on the size, absolute or relative, of pointers.

Is there a fixed-size type capable of holding a void* ? I think std::intptr_t or std::uintptr_t are potential candidates, but I'm not sure which (if either) is correct. This is mostly due to signedness; I'm not sure why there's a signed and an unsigned form of intptr_t, since I wasn't under the impression a pointer's signedness could be controlled. Instinct says I'll want to use uintptr_t since I don't believe pointers can be signed, but then what's intptr_t there for?


A bit more information about my need: I'm working on a DLL and need to guarantee cross-compiler capability if possible. For integer-based types this isn't too bad; I'm able to convert them to std::intXX_t types behind-the-scenes. But one of my DLL's functions does some raw memory manipulations and needs to return a void*. I'm concerned about this causing me all sorts of trouble if I can't properly wrap it, but I don't know what to wrap it with: I've not been able to find any sort of guarantee about the size or data range of a pointer. I know for a fact that the memory itself is accessible outside the DLL; this isn't a problem. But making sure a void* can get from the host EXE to my DLL, or vice versa, safely is a much bigger issue.

cf-
  • 8,598
  • 9
  • 36
  • 58
  • 5
    What do you mean by fixed-size? Do you mean that it will have the same size on any system? If so, the answer is no. But I don't see why you'd need something like that: It's not like you can save a pointer in a file, or send it over the network as you can with integers. – interjay Mar 12 '14 at 13:15
  • 3
    "I know the size of any given pointer is the same on a given system" - not necessarily true. – Karoly Horvath Mar 12 '14 at 13:15
  • All the `intptr_t` and `uintptr_t` give you is the guarantee that the storage will be large enough to hold a pointer. You'll still need to cast it to be useful, and whether it's signed or unsigned won't matter at that point. – Mark Ransom Mar 12 '14 at 13:16
  • 1
    note: you're probably doing sg wrong, and it would be beneficial to tell us *why* you want a fixed size pointer. – Karoly Horvath Mar 12 '14 at 13:16
  • 4
    *Why* would you need fixed-size pointers? The usual reasons for fixed-sized integers are (1) guaranteed range for easier reasoning about overflow and bit fiddling and (2) able to map to a known platform-independent number of bytes for serialization and such. Neither seems to make sense for pointers. You virtually never do arithmetic on pointers that benefits from (1) and serializing pointers has a whole host of other problems that make (2) moot. –  Mar 12 '14 at 13:16
  • _What do you mean by fixed-size? Do you mean that it will have the same size on any system?_ Yes. My reason for needing this is bad and I feel bad, but I can't do anything about it: I need to return a void* over a DLL boundary. For integer-based types, I'm accomplishing this by converting everything to a fixed-size type behind-the-scenes. But pointers are a little harder for me to deal with. – cf- Mar 12 '14 at 13:21
  • 1
    @computerfreaker But that means you only need it fixed for each binary version of the DLL, right? I.e. a DLL built with `sizeof(void*) == 4` will not work on a system where `sizeof(void*) == 8` anyway. – Angew is no longer proud of SO Mar 12 '14 at 13:26
  • _"I know the size of any given pointer is the same on a given system" - not necessarily true._ Mind elaborating, please? I was under the impression a pointer to any given datatype is the same size. I know function pointers are a different sort of animal, but I don't need to interact with those at all. – cf- Mar 12 '14 at 13:28
  • @Angew very good point. So do you think it's acceptable for me to hard-code the size of the `void*` into my DLL, using `#if`/`#elif` to adjust for 32-bit vs 64-bit? – cf- Mar 12 '14 at 13:30
  • "I was under the impression a pointer to any given datatype is the same size." - there are lots of questions about that - see e.g. [here](http://stackoverflow.com/questions/3520059/does-the-size-of-pointers-vary-in-c) – Tony Delroy Mar 12 '14 at 13:38
  • There are few guarantees about pointer sizes in C++. A large swath of pointers are guaranteed to dit in a `void*`, but this does not imclude pointers-to-members. A system whereby pointers-to-`int` are different in size from pointers-to`double` is probably legal. C++ permits a LOT of freedom: specific platforms less so. – Yakk - Adam Nevraumont Mar 12 '14 at 13:42
  • @delnan Actually, neither of those reasons are really valid, in a portable sense. There's really very little reason to ever use `int32_t` and company. – James Kanze Mar 12 '14 at 14:25
  • @computerfreaker I've worked on systems where `sizeof(char*) != sizeof(int*)`. They used to be very common (but I doubt if you'd find one today). – James Kanze Mar 12 '14 at 14:27
  • Thanks all for pointing out that relative pointer size is not guaranteed. I edited my question to note that. _Mea culpa_ - this is what comes of running a bunch of quick-and-dirty `sizeof` tests instead of doing proper research. – cf- Mar 12 '14 at 14:43

6 Answers6

5

intptr_t and uintptr_t are not fixed-size types, so they will not suffice for your stated purpose. You specifically asked for "a fixed-size type capable of holding a void*." That's not 100% possible, because we have no idea what future systems will have as their pointer sizes (128 bits is likely going to happen within our lifetimes).

I suggest you use uint64_t, and add a static_assert(sizeof(void*) <= sizeof(uint64_t)). Someone may come and say that there is no guarantee that putting a void* into a uint64_t will work on all platforms even where that assertion passes, but I think it's good enough for all practical uses.

John Zwinck
  • 239,568
  • 38
  • 324
  • 436
1

I know the size of any given pointer is the same on a given system

Not necessarily. There have been platforms for which char* (and void*) was larger than int*.

Is there a fixed-size type capable of holding a void* ?

No, because the size of void* varies across platforms. There's no (standard) fixed-size type larger than 64 bits, and no guarantee that pointers won't be larger than that.

I think std::intptr_t or std::uintptr_t are potential candidates, but I'm not sure which (if either) is correct.

They are both specified to be large enough to hold any pointer value; but the size might vary between platforms. Choose signed or unsigned based on the type of arithmetic (if any) you want to do with the converted value.

Mike Seymour
  • 249,747
  • 28
  • 448
  • 644
1

Is there a fixed-size equivalent to void*?

But I also know the size of a pointer can vary from system to system (in particular, 32-bit vs 64-bit).

Together, the above question and statement imply you want something not just for the running process, but portable across processes and even systems. All you can do is pick an integral type at least as long as the largest size of any system you'll ever want to support. There is no guarantee that any particular size will last eternally, so you just have to pick what you think's reasonable. If 64 is enough for you, see if you've got a header defining uint64_t.

I think std::intptr_t or std::uintptr_t are potential candidates, but I'm not sure which (if either) is correct.

These are sized based on the running process, and no guaranteed to be large enough to hold pointer values that come from other processes/systems with wider pointer sizes.

Memory addresses are never negative, so using an unsigned type makes sense. An offset from one memory address to another can be signed, which is why there are signed types intended to store such differences....

Community
  • 1
  • 1
Tony Delroy
  • 102,968
  • 15
  • 177
  • 252
1

It's safe to assume that even across different compilers, void* will be compatible for the same architecture.

Since your goal is a DLL, and you can only load DLLs into processes with the same architecture, that should be enough to guarantee interoperability.

Sebastian Redl
  • 69,373
  • 8
  • 123
  • 157
0

I was creating a program that stores (in Java) native pointers to data structures in C++.

Since I wanted my app to be portable, I used a Java long (64-bit integer) to store the pointers, no matter if my system has 32-bit or 64-bit architectures.

So, I would recomment you to store the pointers in int64_t variables.

ebasconp
  • 1,608
  • 2
  • 17
  • 27
0

If you are just concerned in the memory size, both intptr_t and uintptr_t are possible.

If you are concerned about the actual value or do some arithmetic, unsigned one should be better.

But neither is fixed-size, their storage size is implementation dependent.

  • If you are doing arithmetic, the signed one is imperative. Unsigned arithmetic doesn't obey normal mathematical rules. – James Kanze Mar 12 '14 at 14:29
  • I do not agree. With signed, you can easily add a small number and then overflow, whereas you would get the correct answer with unsigned. – Vladimir F Героям слава Mar 12 '14 at 15:17
  • Which would probably still work in the end, but it cannot be guaranteed. – Vladimir F Героям слава Mar 12 '14 at 15:18
  • If you're that close to overflowing, you've done something wrong downstream. The problem is that expressions like `abs( a - b )` give the wrong results with `unsigned` (and such expressions are pretty likely for numerical values). – James Kanze Mar 12 '14 at 15:52
  • Why something wrong? Whole half of the 4GB memory in a 32bit machine is negative in signed. The correct overflow from positive to negative cannot be guaranteed. – Vladimir F Героям слава Mar 12 '14 at 16:02
  • What do you mean by "the whole half of the 4GB memory is negative in signed"? Neither pointers nor iterators are either signed nor unsigned. The size of an array or vector, however, will always be positive, and except for vector of char, it will fit in a signed `ptrdiff_t`. Which is what you should be using if your vectors can reasonable have more elements than `int`. (In most applications, external constraints mean that they can't, or you do validation checks on the input to ensure that they can't.) – James Kanze Mar 12 '14 at 23:21
  • intptr_t is simply an integer, nothing more. A signed integer. That's why it could be negative and there could be overflow from positive to negative. Don§t talk about pointers, we are talking about integers here. – Vladimir F Героям слава Mar 13 '14 at 08:18
  • It's hard to say what we are talking about, since the OP wasn't clear what he really needed. In practice, unsigned integral types are to be avoided any time arithmetic is involved, because they can result in the wrong results even when there is no overflow. – James Kanze Mar 15 '14 at 14:17