Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

When is pointer subtraction undefined in C?

char *buf = malloc(bufsize)
char *ptr = buf;
…
while(condition) {
    ptrdiff_t offset = ptr - buf;    // <========== THIS LINE

    // offset will never be negative because we only ever *increase* ptr
    if ((size_t)offset > bufsize) {
        // we need more room
        bufsize += 128;
        buf = realloc(buf, bufsize);
        ptr = buf + offset;  // buf might be in a completely new location
    }
    *ptr++ = …  // write this byte
}

Is this valid or undefined?

I would have assumed that it's valid, but I read something about it being undefined, so I googled it. These links seem to inescapably claim it's undefined:

  • Secure coding
  • Is subtraction of pointers not pointing to different elements of same array valid in C?

However, no mention of it is made in these SO questions:

  • Pointer subtraction confusion
  • size_t return pointer subtraction
  • Pointer Arithmetic In C

These all talk about not two pointers being in the same "array". Does that actually mean a plain old C array on the stack?

If it is undefined, it seems very odd to me… Why force me to carry along a counter variable when I have access to one constant pointer and one moving pointer?

like image 422
mk12 Avatar asked Aug 23 '12 20:08

mk12


1 Answers

It's defined behavior, as long as you don't go farther than one element past the end of the array. C99 §6.5.6/8 says this about adding a pointer and an integer:

[...] If both the pointer operand and the result point to elements of the same array object, or one past the last element of the array object, the evaluation shall not produce an overflow; otherwise, the behavior is undefined. [...]

And paragraph 9, on subtraction:

9) When two pointers are subtracted, both shall point to elements of the same array object, or one past the last element of the array object; [...]

And from §7.20.3/1:

The pointer returned if the allocation succeeds is suitably aligned so that it may be assigned to a pointer to any type of object and then used to access such an object or an array of such objects in the space allocated (until the space is explicitly deallocated).

So once you move ptr to point beyond the element after the last array element, doing the pointer subtraction is undefined behavior.

I do believe there are systems out there that would behave badly with this code, although I can't name any. In theory, malloc() could return a pointer to just before the end of addressable memory, e.g. if you ask for 255 bytes it could return 0xFFFFFF00 on a 32-bit system, so creating a pointer beyond the end will lead to overflow. It's also possible that integer overflow on pointer representations can trigger a trap of some sort (e.g. if pointers are stored in special registers). While I don't know of any systems with these properties, the C standard certainly allows for their existence.

like image 136
Adam Rosenfield Avatar answered Oct 22 '22 05:10

Adam Rosenfield