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?

- 24,991
- 10
- 72
- 122
-
4Umm.. isn't that what void* is for? – mjs Apr 22 '15 at 14:21
-
2I don't want clients of this struct to ever think that the _pointee_ value is of any interest whatsoever, and by making the field an `intptr_t` or `uintptr_t` I hope to make it clear that it's the _pointer_ value itself that is interesting. Please let me know if I'm barking up the wrong tree in reasoning like that :-) – Johann Gerell Apr 22 '15 at 14:25
-
2That's your interpretation: others might not even know what `intptr_t` is for. I'd use a `void*` and a *comment*, to point out you only care about the memory address. Something like: `void *address; /* We just care about the memory address */`. – edmz Apr 22 '15 at 14:42
-
@JohannGerell: As mjs says, that's exactly what `void*` is for. The integer types `intptr_t` and `uintptr_t` aren't even guaranteed to exist. – Keith Thompson Apr 22 '15 at 15:10
-
[What is the use of intptr_t?](https://stackoverflow.com/q/35071200/995714) – phuclv Oct 09 '22 at 09:16
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)

- 223,805
- 18
- 296
- 547
-
I worked on a Cray T90 running Unicos. `long*` and `char*` were the same size (64 bits), but `char*` used the high-order 3 bits to store a byte offset, since machine addresses pointed to 64-bit words (this was all managed in software). Was the older Y-MP different? – Keith Thompson Apr 22 '15 at 15:13
-
I forgot the details, but casting (I forgot in what direction) `(char*)` to/from `(long*)` was an issue... – Basile Starynkevitch Apr 22 '15 at 15:15
-
2On the T90, integer and pointer conversions just copied the bits. If a `char*` pointer has a non-zero offset, converting it to `long*` would yield an invalid pointer, since the 3 high-order bits would be part of the address (and the actual address space wasn't nearly that big). I once saw some code that computed the difference between two pointers by first converting them to integers; I pointed out that direct pointer subtraction is simpler and has the virtue of actually working. – Keith Thompson Apr 22 '15 at 15:25
-
It sounds like OP is treating the value as an opaque handle, in which case using (u)intptr_t rather than void * gives them the freedom of changing their implementation between ptr and array index without changing their API. – JimKleck Mar 26 '20 at 23:17
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).

- 391,730
- 64
- 469
- 606
-
_"That sounds very strange, since it's going to require casts"_ - well, yes. I don't want clients of this struct to ever think that the _pointee_ value is of any interest whatsoever, and by making the field an `intptr_t` or `uintptr_t` I hope to make it clear that it's the _pointer_ value itself that is interesting. Please let me know if I'm barking up the wrong tree in reasoning like that :-) – Johann Gerell Apr 22 '15 at 14:26
-
4@JohannGerell It's hard to be definitive, of course. But since the entire point of `void *` is that it can't be dereferenced since there's no type information about the pointee, I'd say that's exactly what it communicates already. – unwind Apr 22 '15 at 14:28
-
1you might need to use `intptr_t` to get [signed extension on x86_64 canonical addresses](http://stackoverflow.com/q/16198700/995714) – phuclv Dec 04 '16 at 11:47
-
`void*` rather comes with a list of huge disadvantages. I've posted an answer showing some of the most common causes for bugs. – Lundin Sep 26 '18 at 14:21
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?.

- 195,001
- 40
- 254
- 396
-
Another consideration is when you are using the value as a handle and passing it around. With it defined as (u)intptr_t your local implementation can change from ptr to array index without changing the API. – JimKleck Mar 26 '20 at 23:14
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).

- 563
- 2
- 5