6

I learned by this question that incrementing a NULL pointer or incrementing past the end of an array isn't well-defined behavior:

int* pointer = 0;
pointer++;

int a[3];
int* pointer2 = &a[0];
pointer2 += 4; 

But what If the pointer pointing to a invalid place is only used for comparison and memory at his location is never accessed?

Example:

void exampleFunction(int arrayLen, char** strArray)
{
    for(char** str = strArray; str < strArray + arrayLen; str++) //or even str < &strArray[arrayLen]
    {
        //here *str is always a pointer to the first char of my string
    }
}

Here I compare my pointer to a pointer one element past the end of the array. Is this well-defined behavior?

Winter
  • 3,894
  • 7
  • 24
  • 56
  • When would the expression `*argv + argc` ever make sense? The length of `argv[0]` isn't related to `argc`. If you meant `argv + argc`, it's fine AIUI because pointers "one element past the end of the array" are well-defined and can be compared to other pointers in the same array. (Or `argv + argc + 1` because, as chux mentioned, `argv` ends with `NULL`.) – Ry- Jul 26 '17 at 19:17
  • @chux Good to know ! I just have to use `<=` then. But the original question shouldn't take my mistake in consideration. Updated with better example – Winter Jul 26 '17 at 19:19
  • Your second code is OK as `str` is always a valid pointer. It last value is not de-reference-able though. – chux - Reinstate Monica Jul 26 '17 at 19:23
  • The edited expression `*strArray + arrayLen` still doesn't make sense. – Ry- Jul 26 '17 at 19:32
  • Incrementing *one step* past the end of the array is fine. Incrementing any further is not. `pointer2 += 4` is undefined behavior, but `pointer2 += 3` would not be, even though it would cause `pointer2` to point past the end of the array. – user2357112 Jul 26 '17 at 20:08
  • Note that "Is comparing to a pointer one element past the end of an array well-defined?" does not apply to the UB of `int* pointer = 0; pointer++;`. – chux - Reinstate Monica Jul 26 '17 at 20:29
  • @chux The point was "does it matter if I create a pointer pointing to nothing as long as I don't use it to access" but the question was narrowed a bit to the example I provided. – Winter Jul 26 '17 at 20:45

1 Answers1

8

Comparing to a pointer one step past the end of an array is well defined. However, your pointer and pointer2 examples are undefined, even if you do literally nothing with those pointers.

A pointer may point to one element past the end of the array. That pointer may not be dereferenced (otherwise that would be undefined behavior) but it can be compared to another pointer within the array.

Section 6.5.6 of the C standard says the following regarding pointer addition (emphasis added):

8 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. If the result points one past the last element of the array object, it shall not be used as the operand of a unary * operator that is evaluated.

Section 6.5.8 says the following regarding pointer comparisons (emphasis added):

5 When two pointers are compared, the result depends on the relative locations in the address space of the objects pointed to. If two pointers to object types both point to the same object, or both point one past the last element of the same array object, they compare equal. If the objects pointed to are members of the same aggregate object, pointers to structure members declared later compare greater than pointers to members declared earlier in the structure, and pointers to array elements with larger subscript values compare greater than pointers to elements of the same array with lower subscript values. All pointers to members of the same union object compare equal. If the expression P points to an element of an array object and the expression Q points to the last element of the same array object, the pointer expression Q+1 compares greater than P. In all other cases, the behavior is undefined.

In the case of pointer1, it starts out pointing to NULL. Incrementing this pointer invokes undefined behavior because it don't point to a valid object.

For pointer2, it is increased by 4, putting it two elements past the end of the array, not one, so this is again undefined behavior. Had it been increased by 3, the behavior would be well defined.

pyrmont
  • 225
  • 5
  • 12
dbush
  • 205,898
  • 23
  • 218
  • 273
  • 2
    Is not the "one passed" rule applies to non-array objects too? `int x; int *p = &x+1;` – chux - Reinstate Monica Jul 26 '17 at 19:26
  • 2
    @chux You may be right. Section 6.5.8 part 4 says *"For the purposes of these operators, a pointer to an object that is not an element of an array behaves the same as a pointer to the first element of an array of length one with the type of the object as its element type.*" Section 6.5.6 part 7 has the same wording. – dbush Jul 26 '17 at 19:36
  • The quoted 6.5.8 applies to `<`, `<=`, `>=`, and `>`. The `==` and `!=` operators never yield Undefined Behavior when given pointers to objects, pointers "just past" objects, or null pointers. A comparison between a pointer "to" one object and a pointer "just past" another may report equality, but that does not imply the pointers may be used interchangeably. – supercat Jul 26 '17 at 22:45
  • 1
    Does this also apply to the element one before the array? I'm asking because I'm needing to iterate in reverse and the nicest idiom would be a pointer one step before the first element. – Levi Morrison Jun 30 '18 at 00:24
  • 1
    @LeviMorrison it does not – dbush Jun 30 '18 at 00:26
  • @LeviMorrison: IMHO, the best way to understand the allowance for "just-past" pointers is to recognize that each object has two associated addresses: one for the start and one for the end. An array of three elements will have four associated address: start of first, between first and second, between second and third, and end of third. – supercat Jun 30 '18 at 17:50
  • @LeviMorrison Start with one-past-the-end, pre-decrement instead of post-decrementing, and use `>= start` instead of `< end` as the loop predicate. – mtraceur Oct 22 '21 at 07:07