Which integer type can be safely be used to always hold a pointer value? Would it be std::intptr_t
? But I thought int
would also always hold a pointer value and is present in every build, and stuff like int32_t
or int64_t
would be changed if the input size is not exact
-
1Why? Use a `void *`. – user207421 Mar 19 '19 at 01:11
-
If you want to use an integer type, `size_t` is the best bet. – Sid S Mar 19 '19 at 02:21
-
1Possible duplicate of [Converting a pointer into an integer](https://stackoverflow.com/q/153065/608639), [What is uintptr_t data type](https://stackoverflow.com/q/1845482/608639), [unsigned int vs. size_t](https://stackoverflow.com/q/131803/608639), etc. – jww Mar 19 '19 at 03:00
-
1Also be careful of old memory models, like x86 segmented addresses. If I recall, `size_t` and pointers are different sizes (this is a almost never a problem in practice). – jww Mar 19 '19 at 03:06
2 Answers
Which integer type can be safely and portably used to always hold a pointer value:
Would it be std::intptr_t?
The entire idea of storing a pointer value in an integer is not necessarily fully portable to all systems.
std::intptr_t
is the most appropriate choice on systems where it is provided. And correspondingly, std::uintptr_t
.
But I thought int would also always hold a pointer value
You were mistaken. int
is not guaranteed to always be able to represent all pointer values. In fact, on the most common 64 bit systems - where pointers are 64 bit - int
is 32 bits.
For reasoning why that is, here is a document outlining the reasons in the case of UNIX/POSIX: http://www.unix.org/whitepapers/64bit.html
You should also consider why you would want to do such thing in the first place. I can think of a few reasons:
- In order to calculate the alignment of the address. This should be no longer necessary since C++11 which has
std::align
. - In order to further convert the integer into a string for debugging purposes. The standard library already has ways of converting a pointer to a string (
std::printf
family of functions and character streams). But those are not available in all contexts (signal handlers; freestanding implementations), and the pointer to integer conversion might therefore make sense in those contexts. - Calculating a hash.
std::hash
(C++11) already has a specialisation for pointers, but if you find your standard library implementation insufficient, you could use a custom hash function after converting to integer. - Storing data in "unused" bits of a pointer. This optimisation technique is highly implementation dependent. Low order bits of pointers with alignment greater than 1 can be used. 16 high order bits can be used in x86-64 architecture (which may shrink to [possibly to 7] in future: https://software.intel.com/sites/default/files/managed/2b/80/5-level_paging_white_paper.pdf).
-
Except that `intptr_t` and `uintptr_t` are optional in the standard. For some architectures, it might not be possible to represent an address with an integer of supported size. So not 100% portable, but at least they will either work as expected, or fail to compile. – aschepler Mar 19 '19 at 01:55
-
2@aschepler I incorporated some of your points into my answer. Out of curiosity, do you know of any such architecture, which also has a C++ compiler? – eerorika Mar 19 '19 at 02:05
-
-
I've been collecting a [list of platforms](https://github.com/o11c/targets) - in all my data, char/short/int/long/long-long/ptr is are always 8, 16, 32, or 64 bits (although there is sometimes padding for pointers). Note that there *may* be more than kind of pointer (like `near` vs `far`), and the non-default ones may or may not be backed by int24_t or similar types that don't exist in standard C. – o11c Mar 19 '19 at 03:05
-
@eerorika I know one use of integers to store pointers: save space. There are huge structures such as [Binary Decision Diagram (BDD)](https://en.wikipedia.org/wiki/Binary_decision_diagram). The least significant bit of each child tells if the child is negated or not, while all other bits contain the address of the child. With BDDs it is not uncommon to run out of RAM, so every bit counts. – Michael Veksler Mar 19 '19 at 08:09
-
@eerorika you save space for the flags. The flags are stored at the LSB or-ed with the address. The address must be aligned, so the lsb is zeroed before dereferencing – Michael Veksler Mar 19 '19 at 10:40
-
@MichaelVeksler yeah. It took me a while to get what you wrote. I'm adding a generic version to the answer. – eerorika Mar 19 '19 at 10:41
Yes, std::intptr_t is a better option. It is defined to different width to save address as a pointer on x64 or x86 system.
// Definitions of common types
#ifdef _WIN64
typedef unsigned __int64 size_t;
typedef __int64 ptrdiff_t;
typedef __int64 intptr_t;
#else
typedef unsigned int size_t;
typedef int ptrdiff_t;
typedef int intptr_t;
#endif

- 459
- 5
- 15
-
But it isn't defined in such a way as to guarantee what the OP wants. – user207421 Mar 19 '19 at 11:11