0

I was trying to make a 'smart' optimization when I stumbled upon some weird behavior, so I created a minimal example to replicate it.

In the code below, I create an array and shift elements to the left, then print the array. The code works exactly like one would expect.

#include <iostream>

int main()
{
    int array[5]{0,1,2,3,4};

    for (int i = 0; i < 4; ++i)
    {
        array[i] = array[i + 1];            // Array becomes {1,2,3,4,4}
    }

    std::cout << "Printing array: ";
    for (int i = 0; i < 5; ++i)
    {
        std::cout << array[i];              // Prints {1,2,3,4, 4}
    }
    std::cout << "\n";
}

Now, looking at the first for block, I thought I could spare one i increment. So I tried to rewrite that block as follows:

    for (int i = 0; i < 4;)
    {
        array[i] = array[++i];          // Expected: array becomes {1,2,3,4,4}
    }

This does not work, however. i is in fact updated, because the program does not loop, but the assignment does not go as expected and the terminal outputs Printing array: 01234.

Now, it might have been that the expression on the right is evaluated before the expression on the left, in which case this behavior would be explained. However, I also tried the following reverse iteration:

    for (int i = 4; i > 0;)
    {
        array[--i] = array[i];          // Expected: array becomes {1,2,3,4,4}
    }

And it also outputs Printing array: 01234.

In both cases, I tried compiling in debug mode, in case it was some weird compiler optimization responsible for it, but the result was still the same.

  • 3
    Before C++17, `array[i] = array[++i];` is UB. – Jarod42 Mar 13 '20 at 17:57
  • 2
    Does this answer your question? [Undefined behavior and sequence points](https://stackoverflow.com/questions/4176328/undefined-behavior-and-sequence-points) (the top answer addresses this specific case) – ShadowRanger Mar 13 '20 at 17:57
  • 2
    *"I was trying to make a 'smart' optimization"* Let compilers do those kind of optimizations. – Jarod42 Mar 13 '20 at 18:04

2 Answers2

3

Prior to C++17, it is undefined behavior,

Since C++17, we have

Order of evaluation

20) In every simple assignment expression E1=E2 and every compound assignment expression E1@=E2, every value computation and side-effect of E2 is sequenced before every value computation and side effect of E1.

So

array[i] = array[++i];

array[++i] is evaluated first, (incrementing so i)
array[i] is evaluated after, with i incremented.

So it is mostly equivalent to

++i;
array[i] = array[i]; // Noop

And for

array[--i] = array[i];

it is mostly equivalent to

array[i - 1] = array[i];
--i
Jarod42
  • 203,559
  • 14
  • 181
  • 302
1

You are invoking undefined behavior because the order of evaluation in an expression is unspecified. Don't read and write from/to an object in the same expression. The offending lines are:

array[i] = array[++i];

and:

array[--i] = array[i];

This was changed in the C++17 standard and if you compiled with C++17 support, you would not invoke undefined behavior.

Ron
  • 14,674
  • 4
  • 34
  • 47