为什么要始终按预期工作的UB更改UB

Why change UB that will always work as intended?

本文关键字:UB 工作 更改 为什么      更新时间:2023-10-16

在我正在处理的旧代码库中,我发现了行

n = ++n % size;

这只是预期

的不良措辞
n = (n+1) % size;

从周围的代码和运行时推论得出。(后者现在取代了前者。)

但是,由于此代码被CPPCKECK标记为错误,并在GCC中引起了警告,而没有引起任何故障,所以我并没有停止在这里思考。我将线路减少到

n = ++n;

仍然获取原始错误/警告消息:

cppcheck 1.80:

id:未知词
摘要:表达'n = n'取决于评估副作用的顺序
消息:表达式'n = n'取决于副作用评估顺序

GCC(Mingw32-G 。EXE,版本4.9.2,C 98):

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

我已经了解到,C/C 中的分配表达式可能会受到未定义的评估顺序的影响,但是在这种情况下,我无法想象。

n = ++n;不确定的评估顺序是否确实与结果程序相关,尤其是对于n的预期值?这就是我想象的可能发生的事情。

Scenario #1
++n;
n=n;
Scenario #2
n=n;
++n;

我知道,对C 中不确定行为的含义和含义很难理解,也很难教导。

我知道n=++n;的行为是在C 11之前由C 标准不确定的。但是它具有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)给出易于理解的 dixplantion 该代码如何会产生错误的结果吗?


[1] 编译器已使用的

  • Borland-C 5.3.0(Pre-C 98)
  • Borland-C 5.6.4(C 98)
  • C (VC )
  • C (GCC 6.3)
  • C 14(GCC 6.3)
  • C 14 clang

精确定义了增量顺序。有人说

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

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

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

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

根据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