问题
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 correct to store it as an intptr_t
than a void*
. The question is whether a uintptr_t
is better suited or not, and when one is preferred over the other in general?
回答1:
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)
回答2:
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).
回答3:
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:
- All manner of different pointer types are most often not compatible with each other and cannot alias. This is a problem with object pointers as well as function pointers.
- You often have type qualifiers like
const
, which makes pointer conversions to/from that type questionable or poorly-defined. - Conversions to/from
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. - Performing arithmetic on a pointer comes with numerous pitfalls, because you can only safely do arithmetic on a pointer which points at an allocated array. However, one can often have a memory address for a lot of other reasons than pointing at an array, as anyone working with embedded systems knows.
- You can't even perform pointer arithmetic calculations on a
void*
. Doing so relies on non-standard compiler extension.
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?.
回答4:
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).
来源:https://stackoverflow.com/questions/29800256/when-is-uintptr-t-preferred-over-intptr-t