What is uintptr_t
and what can it be used for?
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".
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 ptrdiff_t
. uintptr_t
isn't meant for that. –
Musketry unsigned int
usually isn't large enough. But it might be large enough. This type exists specifically to remove all "assuming". –
Glendon 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.
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 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 <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 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 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".
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 ptrdiff_t
. uintptr_t
isn't meant for that. –
Musketry unsigned int
usually isn't large enough. But it might be large enough. This type exists specifically to remove all "assuming". –
Glendon 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.
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 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));
}
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.
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 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.
© 2022 - 2024 — McMap. All rights reserved.
std::uintptr_t
andstd::intptr_t
a C++11 optional. – Lipman