uint8_t * ptr = ...;
uint8_t * ptr2 = ptr + 5;
Now if ptr
was 100
, what will ptr2
be? Correct, it will be 105
. But now look at that code:
uint32_t * ptr = ...;
uint32_t * ptr2 = ptr + 5;
Again, if ptr
was 100
, what will ptr2
be? Wrong! It won't be 105
, it will be 120
.
Why? Pointer arithmetic is not integer arithmetic!
ptr2 = ptr + 5;
Actually means:
ptr2 = int_to_ptr(ptr_to_int(ptr) + (sizeof(ptr) * 5));
Functions int_to_ptr
and ptr_to_int
don't really exist, I'm just using them for demonstration purpose, so you better understand what is going on between the scenes.
So if you subtract two pointers, the result is not the difference of their addresses, it's the number of elements between them:
uint32_t test[50];
ptrdiff_t diff = &test[20] - &test[10];
diff
will be 10, as there are 10 elements in between them (one element is one uint32_t
value) but that doesn't mean there are 10 bytes between test[10]
and test[20]
, there are 40 bytes between them as every uint32_t
value takes up 4 bytes of memory.
Now you may understand why subtracting pointers of different types makes no sense, as different types have different element sizes and what shall such a subtraction then return?
If you want how many bytes are in between two pointers, you need to cast them both to a data type that has one-byte elements (e.g. uint8_t *
or char *
would work) or cast them to void *
(GNU extension but many compilers support that as well), which means the data type is unknown and thus the element size is unknown as well and in that case the compiler will byte-sized elements. So this may work:
ptrdiff_t diff = (void *)ptr2 - (void *)ptr1;
yet this
ptrdiff_t diff = (char *)ptr2 - (char *)ptr1;
is more portable.
It will compile, it will deliver an result. If that result is meaningful, that's a different topic. Unless both pointers point to the same memory "object" (same struct, same array, same allocated memory region), it is not, as the standard says that in that case, the result is undefined. That means diff
could (legally) have any value, so a compiler may as well always set diff
to 0 in that case, that would be allowed by the standards.
If you want defined behavior, try this instead:
ptrdiff_t diff = (ptrdiff_t)ptr2 - (ptrdiff_t)ptr1;
That is legal and defined. Every pointer can be casted to an int value and ptrdiff_t
is an int value, one that is guaranteed to big enough so that every pointer can fit into it (don't ever use int
or long
for that purpose, they do not make any such guarantee!). This code converts both pointers to integers and then subtract them. I still don't see anything useful you can do with diff
now, but that code at least will deliver a defined result, yet maybe not the result you might be expecting.