3
#include <iostream>
using namespace std;
int main()
{
    int arr[8];
    int n = 0;
    while (n < 8)
    {
        arr[n] = ++n;
    }
    for (n = 0; n < 8; n++)
    {
        cout << arr[n]<<" ";
    }
}

output- garbage 1 2 3 4 5 6 7 expected output- 1 2 3 4 5 6 7 8

1201ProgramAlarm
  • 32,384
  • 7
  • 42
  • 56
  • `arr[n] = ++n;` iteration `7` invokes undefined behavior indexing beyond the last element of the array. The valid indexes for `arr` are `0 <= n < 8`. – David C. Rankin Dec 21 '21 at 06:31
  • 2
    Additionally see [Undefined behavior and sequence points](https://stackoverflow.com/q/4176328/3422102). If you simply compile with warnings enabled, the compiler will identify these problems for you. – David C. Rankin Dec 21 '21 at 06:39
  • What C++ version are you using? – 김선달 Dec 21 '21 at 08:46

3 Answers3

7

The statement arr[n] = ++n; has Undefined Behavior because it is unspecified if n is incremented before being used as the subscript in arr.

In your case, with your compiler, the increment happens first, so that you never assign anything to arr[0] and write past the end arr[8] of the array.

One way to address this is to split it into two statements:

arr[n] = n;
++n;

The evaluation order is known as sequencing, and the rules have changed as the language has evolved. Of significance, with C++17 the increment of n will happen before calculating the address to store the result in, so you'll always end up with an uninitalized first element and the write past the end of the array.

1201ProgramAlarm
  • 32,384
  • 7
  • 42
  • 56
4

Turn on your warning flags! Your program can have undefined behavior;

main.cpp:34:18: warning: operation on 'n' may be undefined [-Wsequence-point]
   34 |         arr[n] = ++n;
      |                  ^~~
main.cpp:34:16: warning: iteration 7 invokes undefined behavior [-Waggressive-loop-optimizations]
   34 |         arr[n] = ++n;
      |         ~~~~~~~^~~~~
main.cpp:32:14: note: within this loop
   32 |     while (n < 8)

Maybe you could do this instead:

int arr[8] { };
int n = 0;

while ( n < 8 )
{
    arr[n] = n + 1;
    ++n;
}

for ( n = 0; n < 8; ++n )
{
    std::cout << arr[n] << " ";
}
digito_evo
  • 3,216
  • 2
  • 14
  • 42
0

In C++ the expression on the right hand side (RHS) of the = sign is evaluated first, because you can't store (assign) a value in a variable if you haven't calculated what it is!

int myAge = 10 + 10; // calculates 20 first, then stores it.

You're changing the value of n before assigning to position n in your array - increasing it from 0 to 1 in the first iteration of the loop - and so assigning the result, 1, to array[1].

Why? Well, ++n and n++ are complicated (and frequently confusing) operators in c++, in that that they're both mathematical and assignment operators. ++n is essentially equivalent to:

n = n + 1;
return n;

Where n++ is closer to:

n = n+1;
return n -1;

You can code it more explicitly with simpler operators:

arr[n] = n+1;
n = n+1;

.. or use a for loop as you did in your output code. Using the same looping structures for both might help you achieve consistent outcomes.

You're not alone in struggling with this one. Chris Lattner felt the ++ and -- unary operators' potential for obfuscation and bugs sufficiently outweighed their benefits that they were dropped from Swift 3.

dmac
  • 9
  • 2
  • You are wrong about the issue. It's not that it's confusing to have both `n` and `n++` in the same expression, it's flat out wrong. – Sebastian Redl Dec 21 '21 at 07:53
  • Sure it's flat out wrong, that's why he's here asking for help; he's looking for the _why_. I feel that he fell into a common trap of not fully appreciating all the implications of using ++n, something I see a lot among new C++ programmers. I'll reword my answer to make that clearer, thanks for the feedback. – dmac Dec 22 '21 at 00:32
  • But your answer isn't the right *why*. Your very first sentence is wrong. There's no execution ordering between the left and right side of an assignment. Sure, the right side result needs to be calculated before the actual writing happens, but 1) any side effects of the right side could still occur after the value is written, and 2) if the left side is an expression more complex than just a variable access, it could be evaluated before the right side. Or intermingled. And to top it off, your first code example isn't assignment, but initialization, which matters for types more complex than int. – Sebastian Redl Dec 22 '21 at 09:10