What is the uintptr_t data type?
Asked Answered
P

6

350

What is uintptr_t and what can it be used for?

Proliferation answered 4/12, 2009 at 7:52 Comment(2)
Additional details on this type as well as other related types is available here: opengroup.org/onlinepubs/000095399/basedefs/stdint.h.htmlPreponderant
en.cppreference.com/w/cpp/types/integer lists std::uintptr_t and std::intptr_t a C++11 optional.Lipman
G
267

uintptr_t is an unsigned integer type that is capable of storing a data pointer (whether it can hold a function pointer is unspecified). Which typically means that it's the same size as a pointer.

It is optionally defined in C++11 and later standards.

A common reason to want an integer type that can hold an architecture's pointer type is to perform integer-specific operations on a pointer, or to obscure the type of a pointer by providing it as an integer "handle".

Glendon answered 4/12, 2009 at 7:55 Comment(8)
Note that size_t only needs to be sufficient to hold th size of the largest object, and can be smaller than a pointer. This would be expected on segmented architectures like the 8086 (16 bits size_t, but 32 bits void*)Marabout
for representing the difference between two pointers, you have ptrdiff_t. uintptr_t isn't meant for that.Musketry
@jalf: For difference, yes, but for distance, you would want an unsigned type.Glendon
uintptr_t definition is apparently not obligatory (so not standard) even in C++11! cplusplus.com/reference/cstdint (I got the hint from Steve Jessop answer)Underhill
Can't regular unsigned ints (assuming they're at least as large as the size of your memory) store a pointer? after all, they're simply a numerical address to a block of memory...Blasius
@Blasius unsigned int usually isn't large enough. But it might be large enough. This type exists specifically to remove all "assuming".Glendon
Note, uintptr_t can hold ever value of every data pointer, but may not of every function pointer. (same as void*)Saied
@DrewDormann please make it more explicit that uintptr_t is available in C as well (say it briefly in your own words and give a quick link to Steve Jessop's answer).Rh
U
309

First thing, at the time the question was asked, uintptr_t was not in C++. It's in C99, in <stdint.h>, as an optional type. Many C++03 compilers do provide that file. It's also in C++11, in <cstdint>, where again it is optional, and which refers to C99 for the definition.

In C99, it is defined as "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".

Take this to mean what it says. It doesn't say anything about size.

uintptr_t might be the same size as a void*. It might be larger. It could conceivably be smaller, although such a C++ implementation approaches perverse. For example on some hypothetical platform where void* is 32 bits, but only 24 bits of virtual address space are used, you could have a 24-bit uintptr_t which satisfies the requirement. I don't know why an implementation would do that, but the standard permits it.

Unbar answered 4/12, 2009 at 12:26 Comment(7)
Thanks for the "<stdint.h>". My application didn't compile because of uintptr_t declaration. But when I read your comment I add "#include <stdint.h>" and yeah now it works. Thanks!Judaist
To allocate aligned memory, among other uses, for instance?Olfe
Another common use case of casting a pointer to an int is to create an opaque handle that hides the pointer. This is useful for returning a reference to an object from APIs where you want to keep the object private to the library and prevent applications from having access to it. The application is then forced to use the API to perform any operations on the objectStryker
@JoelCunningham: that works but isn't really any different from using void*. It affects possible future directions, though, especially if you might want to change to use something that really is just an integer handle, not a converted pointer at all.Unbar
@CiroSantilli烏坎事件2016六四事件法轮功 A common use case is to pass just an int to an API which expects a void* to generic data. Saves you the keystrokes typedef struct { int whyAmIDoingThis; } SeriouslyTooLong; SeriouslyTooLong whyAmNotDoneYet; whyAmINotDoneYet.whyAmIDoingThis = val; callback.dataPtr = &whyAmINotDoneYet;. Instead: callback.dataPtr = (void*)val. On the other side, you of course get void* and have to cast it back to int.Luthuli
The one in <cstdint> is in the std namespace, of course - it might be germane to mention that, as I commonly see code which assumes that <cstdint> necessarily declares the global-namespace version (which is permitted, but not required).Gardant
How would 24 bits of virtual address in a 32 bit address space allow uintptr_t to be represented in 24 bits? void * is char aligned isn't it? So there is no way we could regain back those last 8 bits when converting back from such a uintptr_t to a void *Lurette
G
267

uintptr_t is an unsigned integer type that is capable of storing a data pointer (whether it can hold a function pointer is unspecified). Which typically means that it's the same size as a pointer.

It is optionally defined in C++11 and later standards.

A common reason to want an integer type that can hold an architecture's pointer type is to perform integer-specific operations on a pointer, or to obscure the type of a pointer by providing it as an integer "handle".

Glendon answered 4/12, 2009 at 7:55 Comment(8)
Note that size_t only needs to be sufficient to hold th size of the largest object, and can be smaller than a pointer. This would be expected on segmented architectures like the 8086 (16 bits size_t, but 32 bits void*)Marabout
for representing the difference between two pointers, you have ptrdiff_t. uintptr_t isn't meant for that.Musketry
@jalf: For difference, yes, but for distance, you would want an unsigned type.Glendon
uintptr_t definition is apparently not obligatory (so not standard) even in C++11! cplusplus.com/reference/cstdint (I got the hint from Steve Jessop answer)Underhill
Can't regular unsigned ints (assuming they're at least as large as the size of your memory) store a pointer? after all, they're simply a numerical address to a block of memory...Blasius
@Blasius unsigned int usually isn't large enough. But it might be large enough. This type exists specifically to remove all "assuming".Glendon
Note, uintptr_t can hold ever value of every data pointer, but may not of every function pointer. (same as void*)Saied
@DrewDormann please make it more explicit that uintptr_t is available in C as well (say it briefly in your own words and give a quick link to Steve Jessop's answer).Rh
L
31

It's an unsigned integer type exactly the size of a pointer. Whenever you need to do something unusual with a pointer - like for example invert all bits (don't ask why) you cast it to uintptr_t and manipulate it as a usual integer number, then cast back.

Loss answered 4/12, 2009 at 7:56 Comment(6)
Of course you could do that, but that would of course be undefined behavior. I believe then only thing you can do with the result of a cast to uintptr_t is to pass it on unchanged and cast it back - everything else is UB.Coincidental
There are times where you need to play with the bits, and it would normally generate compiler errors. A common example is enforcing 16-byte aligned memory for certain video and performance critical applications. stackoverflow.com/questions/227897/…Hanyang
@Coincidental that's not true. On machines that have self-aligned types the two least significant bits of a pointer are going to be zero (because addresses are multiples of 4 or 8). I've seen programs that exploit this to compress data..Cardcarrying
@saadtaame: I just pointed out that this is UB according to the C standard. That does not mean it cannot be defined on some systems - compilers and runtimes are free to define a specific behaviour for something that is UB in standard C. So there's no contradiction :-).Coincidental
It's not necessarily exactly the size of a pointer. All the standard guarantees is that converting a void* pointer value to uintptr_t and back again yields a void* value that compares equal to the original pointer. uintptr_t is usually the same size as void*, but that's not guaranteed, nor is theree any guarantee that the bits of the converted value have any particular meaning. And there's no guarantee that it can hold a converted pointer-to-function value without loss of information. Finally, it's not guaranteed to exist.Ruben
One use is xor linked lists for example.Caresse
S
20

There are already many good answers to "what is uintptr_t data type?". I will try to address the "what it can be used for?" part in this post.

Primarily for bitwise operations on pointers. Remember that in C++ one cannot perform bitwise operations on pointers. For reasons see Why can't you do bitwise operations on pointer in C, and is there a way around this?

Thus in order to do bitwise operations on pointers one would need to cast pointers to type uintptr_t and then perform bitwise operations.

Here is an example of a function that I just wrote to do bitwise exclusive or of 2 pointers to store in a XOR linked list so that we can traverse in both directions like a doubly linked list but without the penalty of storing 2 pointers in each node.

 template <typename T>
 T* xor_ptrs(T* t1, T* t2)
 {
     return reinterpret_cast<T*>(reinterpret_cast<uintptr_t>(t1)^reinterpret_cast<uintptr_t>(t2));
  }
Sunbreak answered 24/12, 2015 at 22:36 Comment(2)
other than bit arithmetic it's also nice if you want to have semantics based on addresses instead of object counts.Crippen
Hah, the Xor Linked List also is the reason why I can find this reason.Logogram
W
11

Running the risk of getting another Necromancer badge, I would like to add one very good use for uintptr_t (or even intptr_t) and that is writing testable embedded code.

I write mostly embedded code targeted at various arm and currently tensilica processors. These have various native bus width and the tensilica is actually a Harvard architecture with separate code and data buses that can be different widths.

I use a test driven development style for much of my code which means I do unit tests for all the code units I write. Unit testing on actual target hardware is a hassle so I typically write everything on an Intel based PC either in Windows or Linux using Ceedling and GCC.

That being said, a lot of embedded code involves bit twiddling and address manipulations. Most of my Intel machines are 64 bit. So if you are going to test address manipulation code you need a generalized object to do math on. Thus the uintptr_t give you a machine independent way of debugging your code before you try deploying to target hardware.

Another issue is for the some machines or even memory models on some compilers, function pointers and data pointers are different widths. On those machines the compiler may not even allow casting between the two classes, but uintptr_t should be able to hold either.

-- Edit --

Was pointed out by @chux, this is not part of the standard and functions are not objects in C. However it usually works and since many people don't even know about these types I usually leave a comment explaining the trickery. Other searches in SO on uintptr_t will provide further explanation. Also we do things in unit testing that we would never do in production because breaking things is good.

Weatherley answered 10/9, 2019 at 19:32 Comment(4)
Not sure if relevant... Have you tried "opaque typedefs"? Vide: youtube.com/watch?v=jLdSjh8oqmEFitzgerald
@Coincidental I wish that was available in C. But having stdint.h is better than nothing. (Also wish enums where stronger typed but most debuggers do a good job of figuring them out any way)Weatherley
"but uintptr_t should be able to hold either." --> Not quite. uintptr_t good to convert to/from a void * and void * to/from _object pointers. A function pointer may not round-trip with a void *- (e.g. function pointer bigger than void *) and by extension, not fit in a uintptr_t.Galenical
@chux, Yes, this is not portable in some limited case. Will add a note. It has been many years since I even looked at intel-16bit stuff but segments do make a mess of things.Weatherley
S
0

Another use case of uintptr_t.

Embedded systems and device drivers frequently use memory-mapped IO (MMIO) for interacting with hardware through so-called peripherals. Each peripheral is described by its size and location in memory — or, its size and a pointer.

So these APIs end up containing endless peripheral address definitions of heroic proportion, and among these definitions are groups of peripherals that you wish to treat similarly, but without writing redundant code.

C++ templates allow you to write generic software, and template specialization in particular is an efficient technique for handling special cases within generic software.

Template specialization often requires you to wrap values in a type of singleton class for any type that isn't an integer. However, casting our peripheral pointers to uintptr_t allows the programmer to use these objects directly in template specializations — as non-type template parameters — without the overhead of wrapping each peripheral in its own type definition.

Swim answered 5/2 at 4:32 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.