1

Let's say that I have a pointer to int that has a value of 0x5. I want to add an offset of 0x3 to that value.

Should I do it like this (method A):

int* pointer = 0x5;
int offset = 0x3;

pointer = pointer + (offset/sizeof(int)); // pointer is now equal to 0x8

or like this (method B):

uintptr_t pointer = 0x5;
pointer = pointer + 0x3;

int* ptr = (int*) pointer; // pointer is now equal to 0x8

I ask this after seeing this question: What is uintptr_t data type

I was informed to NOT use regular integers and pretend they are pointers.

Community
  • 1
  • 1
user2899050
  • 159
  • 1
  • 1
  • 4
  • 1
    You can also cast to `char*` and add 3: it's going to give the same result. – Sergey Kalinichenko Oct 20 '13 at 01:58
  • Generally, you shouldn't be doing regular arithmetic on pointers. Can you explain what you're actually trying to achieve? (Also, your first example is incorrect.) – pburka Oct 20 '13 at 01:58
  • @pburka: I'm writing a low-level DLL that will be injected in a target process to modify its memory. The offsets is to calculate the addresses of certain values from a base address. – user2899050 Oct 20 '13 at 02:00
  • @dasblinkenlight: Can I guarantee that char will always have a size of one? If not, then I still need to have the `/sizeof(char)` part just in case char changes size. – user2899050 Oct 20 '13 at 02:02
  • If you're just calculating pointers based on offsets, use char* pointers and pointer arithmetic as @dasblinkenlight proposes. – pburka Oct 20 '13 at 02:02
  • 6
    `sizeof(char)==1` is required by the C standard, yes. (More than required; it's a *definition*.) – zwol Oct 20 '13 at 02:03

3 Answers3

2

You cannot perform arithmetic on a uintptr_t in a portable manner. Here's how that type is defined:

7.18.1.4 Integer types capable of holding object pointers

The following type designates a signed integer type with the property that any valid pointer to void can be converted to this type, then converted back to pointer to void, and the result will compare equal to the original pointer:

intptr_t

The following type designates an unsigned integer type with the property that any valid pointer to void can be converted to this type, then converted back to pointer to void, and the result will compare equal to the original pointer:

uintptr_t

These types are optional.

Note that the specification doesn't say anything about how the pointer is represented. On most implementations, arithmetic on uintptr_t values will probably have the expected result, but if you want your code to be portable, you should use pointer arithmetic, as it has well specified semantics.

An example of a platform where uintptr_t arithmetic might have unexpected behavior is IBM's z/OS. In legacy 31-bit mode (yes, 31), the most significant pointer bit is reserved for the system. If you have two pointers a and b which differ only in that bit, comparing those pointers with a==b will return 1 (true) as expected, and subtracting them with a-b will return 0. But if you cast those pointers to uintptr_t values and compare them, == will return 0, and - will return non-0.

Community
  • 1
  • 1
pburka
  • 1,434
  • 9
  • 12
1

Generally, you cast to char * or to unsigned char * and do your arithmetic here - since sizeof(char)==1 by definition, you'll actually be doing "regular" arithmetic, with the benefit that you aren't leaving the "pointers domain" in any way and you aren't violating aliasing rules.

Matteo Italia
  • 123,740
  • 17
  • 206
  • 299
1
int *pointer = 0x5;
int offset = 0x3;
pointer = pointer + (offset/sizeof(int));

Assuming sizeof(int) == 4, the division will evaluate to 0 and the pointer will not be changed. If you had instead not divided by sizeof(int), the pointer would be advanced by 3*sizeof(int) char units.

uintptr_t pointer = 0x5;
pointer = pointer + 0x3;
int *ptr = (int *) pointer;

This does in fact set ptr to the same value that ptr = (int *)0x8 would have. (It is overwhelmingly likely that this is not a valid pointer to anything.)

Neither of the above is the normal way to do this sort of thing in C. The idiomatic technique is to cast to char * and back:

int *pointer = 0x5;
ptrdiff_t offset = 0x3;
pointer = (int *) (((char *)pointer) + offset);
assert (pointer == (int *)0x8);
zwol
  • 135,547
  • 38
  • 252
  • 361
  • 1
    Is there a reason we're avoiding `uintptr_t`? Why is it the normal way to cast to `char*`? – user2899050 Oct 20 '13 at 02:07
  • It's mainly historical - the idiom was developed long before `uintptr_t` was invented. However, even today, with a fully conformant C11 implementation (note: I don't know if there *are* any fully conformant C11 implementations yet), `uintptr_t` is *optional*, and there are real implementations that would find it difficult to provide (e.g. some microcontrollers have 16-bit `int` and 24-bit pointers). If `uintptr_t` is available on a particular implementation, though, I believe the standard does require my third snippet to do exactly the same thing if `char *` is replaced by `uintptr_t`. – zwol Oct 20 '13 at 02:13
  • As far as I can remember, there's no guarantee that arithmetic on a `uintptr_t` will have the same effect as arithmetic on a pointer. It's only guaranteed that you can cast `uintptr_t` values back and forth with `void*` values. Some architectures (e.g. segmented memory models) might implement pointer arithmetic quite differently from integer arithmetic. – pburka Oct 20 '13 at 02:59
  • 1
    @pburka On reflection, you're right. The arithmetic is fully defined but the conversions are implementation-defined in both directions, so the overall result need not be the same as for `char *`. I guess that's another reason to use `char *` for this sort of thing. – zwol Oct 20 '13 at 13:54
  • @pburka Note however that the result of `uintptr_t x = 0x5; x += 0x3; int *y = (int *)x;` *does* have to be the same as the result of `int *y = (int *)0x8;` because the value on the RHS is the same in both cases. I'd edit the incorrect statement out of my comment but it's too late for that. – zwol Oct 20 '13 at 13:57