Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

When is uintptr_t preferred over intptr_t?

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?

like image 879
Johann Gerell Avatar asked Apr 22 '15 14:04

Johann Gerell


4 Answers

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)

like image 169
Basile Starynkevitch Avatar answered Oct 12 '22 18:10

Basile Starynkevitch


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

like image 38
unwind Avatar answered Oct 12 '22 16:10

unwind


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

like image 6
Lundin Avatar answered Oct 12 '22 17:10

Lundin


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

like image 3
user2949652 Avatar answered Oct 12 '22 18:10

user2949652