为什么要改变总是按预期工作的UB?

时间:2017-09-06 09:47:00

标签: c++ undefined-behavior legacy-code pre-increment

在我正在处理的遗留代码库中,我发现了一行

n = ++n % size;

这只是对预期

的错误措辞
n = (n+1) % size;

从周围的代码和运行时证明推导出来。 (后者现在取代了前者。)

但是由于这个代码被Cppckeck标记为错误,并且在GCC中引起了警告,没有造成任何故障,我并没有停止在这里思考。我将线路缩小为

n = ++n;

仍然收到原始错误/警告消息:

Cppcheck 1.80:

  

Id:unknownEvaluationOrder
  摘要:表达式'n = ++ n'取决于副作用评估的顺序   消息:表达式'n = ++ n'取决于副作用评估的顺序

GCC(mingw32-g ++。exe,版本4.9.2,C ++ 98):

  

警告:'n'上的操作可能未定义[-Wsequence-point] |

我已经学会了assignment expressions in C/C++ can be heavily affected by undefined evaluation order,但在这种情况下我无法想象

n = ++n;未定义评估顺序是否真的与生成的程序相关,尤其是n的预期值?这就是我想象的可能发生的事情。

Scenario #1
++n;
n=n;

Scenario #2
n=n;
++n;

我知道继承undefined behaviour in C++的意义和含义很难理解,也难以教导。

我知道在C ++ 11之前,C ++标准未定义n=++n;的行为。但它有一个来自C ++ 11的定义行为,这个(现在是标准定义的行为)与我正在观察的几个编译器 [1] 完全相同的这个小型演示程序

#include <iostream>

using namespace std;

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

有输出

n before: 0
n after: 1

对于所有编译器而言,无论是否按标准定义,行为实际上都是相同的,这是否合理?你可以(a)显示一个反例(b)给出一个易于理解的解释这个代码怎么样会产生错误的结果吗?

[1] 使用的编译器

2 个答案:

答案 0 :(得分:3)

精确定义了increment order。在那里声明

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

由于您使用的是C ++ 11编译器,因此可以按原样保留代码。不过,我认为

的表现力
n = (n+1) % size;

更高。您可以更轻松地找出此代码的作者的意图。

答案 1 :(得分:-1)

根据cppreference

  

如果相对于同一标量对象的另一个副作用,标量对象的副作用未被排序,则行为未定义:

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

对于案例n = ++n;,这将是一个未定义的行为,但我们不关心哪个分配首先发生,n =++n