When is uintptr_t preferred over intptr_t?

前端 未结 4 624
粉色の甜心
粉色の甜心 2021-02-05 09:27

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

相关标签:
4条回答
  • 2021-02-05 10:10

    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)

    0 讨论(0)
  • 2021-02-05 10:16

    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 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?.

    0 讨论(0)
  • 2021-02-05 10:20

    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).

    0 讨论(0)
  • 2021-02-05 10:31

    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).

    0 讨论(0)
提交回复
热议问题