Given the requirement that I need to store the value of a \"generic\" pointer in a struct and have no interest in the pointed-at memory itself, I find it more semantically corre
You should pick the type appropriate for the given system and program. Most of the time, pointers are positive address values, in which case uintptr_t
is the correct type. But some systems use negative addresses as a way to express kernel space, as explained here: Can a pointer (address) ever be negative? This would be the reason why there are two different types.
As for (u)intptr_t
vs void*
for a generic pointer type, the former is preferred in rugged, professional programs. There are many problems/bug sources associated with pointer types:
const
, which makes pointer conversions to/from that type questionable or poorly-defined.void*
and other pointer types happen implicitly, making it easy for bugs related to using the wrong pointer type to slip through unnoticed. This was fixed in C++, but remains a hazard in C. Take for example the old but classic "I forgot to include stdlib.h while using malloc in C90" bug.void*
. Doing so relies on non-standard compiler extensions.That being said, a whole lot of legacy code relies on void
pointers, and it's perfectly fine to use them in a restricted context. Some examples would be canonical code relying on generic callback functions: bsearch
, qsort
, pthreads and similar.
I would however not recommend to use void
pointers when designing new C programs - they are, in my opinion, best regarded as a dangerous feature of the past. There exist better and safer methods of generic C programming nowadays, such as C11 _Generic
, tricks using designated initializers, passing parameters as array pointers (to VLA), bounds-checking at compile time with static_assert
etc. Some examples can be found in my answer here: How to create type safe enums?.
It is mostly a stylistic argument (an optimizing compiler would probably generate the same, or very similar, code). However, pointer compares may be a tricky issue.
Remember than in purely standard C pointer compare is roughly meaningful only for pointers to the same aggregate data. You are probably not allowed to compare two results from malloc
, e.g. to keep a sorted array of pointers.
I would keep them as void*
, or else as uintptr_t
. The signed intptr_t
has the inconvenience to seggregate negative and positive numbers, and where they are coming from significant application pointers, this is probably not welcome.
Notice that a void*
cannot be dereferenced: as an uintptr_t
, you have to cast it to do something useful with the data pointed by the address; however void*
pointers can be passed to routines like memset
PS. I am assuming an ordinary processor (e.g. some x86, PowerPC, ARM, ...) with a flat virtual address space. You could find exotic processors -some DSPs perhaps- with very significant differences (and perhaps on which intptr_t
is not always meaningful; remember that on the 1990s Cray Y-MP supercomputers sizeof(long*) != sizeof(char*)
; at that time C99 did not exist, and I am not sure its <stdint.h>
could be meaningful on such machines)
If you want to manipulate the value arithmetically (e.g., to encrypt it), you have much more flexibility with an unsigned type (where arithmetic wraps around) than with a signed type (where arithmetic overflow gives undefined behavior).
That sounds very strange, since it's going to require casts. A void *
in C has the huge advantage that it converts to/from other object pointer types without casts, which is way clean.
That said uintptr_t
might make sense if you want to do things to the bits of the pointer that you can't do as sensibly with a signed integer (such as shifting them to the right, for instance).