Testing Pointers Using a Relational Operator in a Loop

Suppose I have a for loop that stores zeros in an array using pointers such as:

int *vp, values[5]; for(vp = &values[5-1]; vp >= &values[0]; vp--) *vp = 0; 

Pointers to C say that the problem is related to this loop, because the comparison vp >= &values[0] is undefined because it moves outside the bounds of the array. But how?

+7
source share
3 answers

Assuming the pointer is equivalent to an unsigned integer, we see that the problem only exists if values started at address 0, in which case the pointer will UINT_MAX around after decreasing and become UINT_MAX .

To visualize the problem, let's go step by step, assuming values starts at 0x0:

 iteration 1: vp = 0x4, *vp = 0; iteration 2: vp = 0x3, *vp = 0; iteration 3: vp = 0x2, *vp = 0; iteration 4: vp = 0x1, *vp = 0; iteration 5: vp = 0x0, *vp = 0; iteration 6: vp = 0xFFFFFFFF; *vp = ?? // uh oh! 

Thus, vp will never be less than the minimum value for the pointer (which is 0), and this will lead to an infinite loop (assuming that all memory is writable) or a segmentation error.

This is also undefined behavior according to the standard (since you can address one element after the array, but not earlier than it), but in fact it should never fail in any realistic system.

+2
source

This code is unsafe even if old or exotic processor architectures are excluded.

The optimizer in the compiler contains many rules about the language. When he sees vp >= &values[0] , where values is an array, the optimizer has the right to assume that vp points to an element of the array or one outside the array, because otherwise the expression is not defined by C.

Thus, the rules and mechanisms built into the optimizer can decide that vp >= &values[0] always true, so it can generate code as if it were written for (vp = &values[5-1]; ; vp--) . This results in a loop without a termination condition and further undefined behavior when *vp = 0 is evaluated using vp pointing outside the array.

+4
source
 int values[5]; size_t idx; for(idx=5; idx-- > 0; ) { values[idx] = 0; } 

It may seem strange at first sight, but once you get used to it, it can be a good example.

  • you avoid the (possible) lower level of the pointer value (which is strictly undefined)
  • It is impossible to execute underflow without an inscription type (for example, size_t), which, of course, will lead to illegal access, which is preferable to sneak under the same address below, which will lead to memory corruption, and not to crash.
  • WRT for speed / performance: from the point of view of compilers, pointers and indexing are basically the same. The compiler can even generate the same code for both cases.

BTW: if you insist on using pointers, follow these steps:

 int *vp, values[5]; for(vp = &values[5-1]; vp ; vp = (vp > &values[0]) ? vp-1 : NULL; ) { *vp = 0; } 
0
source

All Articles