Why change UB that will always work as intended?

186 views Asked by At

In the legacy code base I'm working on, I discovered the line

n = ++n % size;

that is just a bad phrasing of the intended

n = (n+1) % size;

as deduced from the surrounding code and runtime-proved. (The latter now replaces the former.)

But since this code was marked as an error by Cppckeck, and caused a warning in GCC, without ever having caused any malfunction, I didn't stop thinking here. I reduced the line to

n = ++n;

still getting the original error/warning messages:

Cppcheck 1.80:

Id: unknownEvaluationOrder
Summary: Expression 'n=++n' depends on order of evaluation of side effects
Message: Expression 'n=++n' depends on order of evaluation of side effects

GCC (mingw32-g++.exe, version 4.9.2, C++98):

warning: operation on 'n' may be undefined [-Wsequence-point]|

I already learned that assignment expressions in C/C++ can be heavily affected by undefined evaluation order, but in this very case I just can't imagine how.

Can the undefined evaluation order of n = ++n; really be relevant for the resulting program, especially for intended value of n? That's what I imagine what may happen.

Scenario #1
++n;
n=n;

Scenario #2
n=n;
++n;

I know that the meaning and implications of relaying on undefined behaviour in C++, is hard to understand and hard to teach.

I know that the behaviour of n=++n; is undefined by C++ standards before C++11. But it has a defined behaviour from C++11 on, and this (now standard-defined behaviour) is exactly the same I'm observing with several compilers[1] for this small demo program

#include <iostream>

using namespace std;

int main()
{
    int n = 0;
    cout << "n before: " << n << endl;
    n=++n;
    cout << "n after: " << n << endl;
    return 0;
}

that has the output

n before: 0
n after: 1

Is it reasonable to expect that the behaviour is actually the same for all compilers regardless of being defined or not by standards? Can you (a) show one counter example or (b) give an easy to understand explanation how this code could produce wrong results?


[1] the compilers a used

2

There are 2 answers

5
schorsch312 On

The increment order is precisely defined. It is stated there that

i = ++i + 2;       // undefined behavior until C++11

Since you use a C++11 compiler, you can leave your code as is is. Nevertheless, I think that the expressiveness of

n = (n+1) % size;

is higher. You can more easily figure out what was intended by the writer of this code.

13
Griffin On

According to cppreference:

If a side effect on a scalar object is unsequenced relative to another side effect on the same scalar object, the behavior is undefined:

i = ++i + 2;       // undefined behavior until C++11
i = i++ + 2;       // undefined behavior until C++17
f(i = -2, i = -2); // undefined behavior until C++17
f(++i, ++i);       // undefined behavior until C++17, unspecified after C++17
i = ++i + i++;     // undefined behavior

For the case n = ++n; it would be an undefined behavior but we do not care which assignment happens first, n = or ++n.