【问题标题】:Undefined behavior in c/c++: i++ + ++i vs ++i + i++ [duplicate]c/c++ 中未定义的行为:i++ + ++i vs ++i + i++ [重复]
【发布时间】:2017-02-15 11:06:40
【问题描述】:

假设我们有以下代码:

int i = 1;
int j = i++ + ++i;

我知道这是一个未定义的行为,因为在分号之前,这是一个序列点,i 的值已经改变了不止一次。这意味着即使运算符加号的优先级是从左到右,编译器也可能有两种可能:

案例 1)

  1. i++的值---i的值为1
  2. ++i的值---i的值为2
  3. 执行运算符加号并将结果3分配给j并执行i++的副作用(此步骤的顺序也未定义但我们不在乎,因为它不会改变结果)

案例 2)

  1. i++的值---i的值为1
  2. i++的副作用---i的值为2
  3. ++i的值---i的当前值为3
  4. 执行运算符加号并将结果为 4 分配给j

如果这里没有问题,我有一个问题:

int j = ++i + i++;

上面的代码仍然是未定义的行为吗?

在我看来,只有一种可能:

  1. ++i的副作用---i的值为2
  2. i++的值---i的值为2
  3. 执行运算符加号并将结果4分配给j并执行i++的副作用(此步骤的顺序也未定义但我们不在乎,因为它不会改变结果)

我说的对吗?

顺便说一句,我已阅读此链接:
Undefined behavior and sequence points

【问题讨论】:

  • 评估您的第二个示例有多种方法,就像评估您的第一个示例有多种方法
  • 您错过了未定义行为的概念。任何事情都有可能发生。程序可能会崩溃。程序可以输出42...等等等等
  • 两者都是UB。而“运算符的优先级”只是如何解析表达式并将括号放在表达式中。
  • 例如:live example 为 clang 打印 4,但如果在命令行 you get 5 中将 clang++ 替换为 g++。此外,两者都对 UB 发出警告。
  • 您将关联性和优先级与评估顺序混淆了。参数以未指定的顺序进行评估。仅当存在多个二元运算符时,关联性才重要。

标签: c++ undefined-behavior side-effects sequence-points


【解决方案1】:
int i = 1;
int j = i++ + ++i;

评估将从右到左进行,即首先处理++i,然后处理i++。所以j 的值为 4。

您将收到一条警告“对 'i' 的操作可能未定义”,但此表达式没有问题。

【讨论】:

  • 依赖未定义的行为没有问题吗?
  • 如果,如您所说,“C++ 不是独立于平台的语言”,那么您的答案不是关于 C++,而是关于在操作系统 Y 和硬件 Z 上带有编译器 X 的 C++。由于 X,y , 和 Z 没有在你的答案中指定,这对我来说似乎不正确。
  • C++ 不是独立于平台的语言。我在许多代码中看到人们使用了很多未定义的语法,例如int arr[4] = {11, 12, 45, 34}; int *p = arr + 1; 在这种情况下,编译器表示行为未定义。但是,如果我们深入研究 C++ 的理论,那么它将清楚地回答将返回哪个值。
  • 实际一点,你能给我举个例子吗,两个运行以下代码会产生不同的输出代码` int i=1; std::cout
  • 是的,我可以举个例子。在 Intel(R) Core(TM) i7-2600K CPU @ 3.40GHz 上的 Ubuntu 14.04 上使用 clang++ 3.6.0-2ubuntu1~trusty1 和 g++ Ubuntu 4.8.4-2ubuntu1~14.04.3,clang++ 编译的程序打印 4,而 g++ 编译的程序打印 5。
【解决方案2】:

不,你不正确。每个表达式++ii++ 都修改了i,因此该语句多次修改了i。那是未定义的行为。

请注意,最新版本的标准不再包含“序列点”的概念。

【讨论】:

  • 该标准的最新版本不再使用术语“序列点”,但这个概念仍然存在。
  • @PeteBecker:序列点的概念意味着事件的全局排序——在序列点之前发生的一切都先于之后发生的一切。然而,在许多情况下,A 跟随 B 且 B 跟随 C 的事实可能并不一定意味着 A 跟随 C。例如,给定 x = y & 1; z = x & 2;,编译器将需要根据值在第二个语句中计算 z这将在第一条语句中分配,但如果没有一个变量是volatile,它可以在读取y之前将写入移动到z
  • @supercat - 你的意思是?在“好像”规则下,这一直是允许的。对于单线程程序,措辞的更改旨在不改变旧措辞下的要求。
  • @PeteBecker - 最近的标准肯定有排序的概念(例如,某些操作可能在另一个之前排序,其他一些操作在语句中是未排序的)。这是否意味着保持“序列点”的概念是有争议的 - 我解释它不是,因为该术语不存在,你解释它是。正如您所说,新要求的措辞是为了避免与旧要求不一致。
【解决方案3】:
int j = ++i + i++;

仍然是未定义的行为,因为 ++ii++ 可以在某些 CPU 的多个管道中同时处理,这将导致不可预知的结果。

【讨论】:

  • 但 C++ 标准运算符加号的优先级是从左到右。那么++ii++怎么可能同时处理呢?
  • @Thomas。没有什么比从左到右的优先级更好的了。有从左到右的关联性。
  • 什么是“从左到右”关联性?
  • @Thomas。回覆。从左到右的关联性。见stackoverflow.com/questions/930486/…
  • @Thomas 运算符优先级定义了对值进行操作的顺序,但未定义表达式(例如++i)何时转换为值(已评估)。这就是使它成为 UB 的原因。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-04-25
  • 2011-10-15
  • 2015-01-09
  • 2011-09-22
  • 2012-11-23
相关资源
最近更新 更多