21

可能重复:
谁能解释这些未定义的行为(i = i++ + ++i,i = i++ 等……)

根据 c++ 标准,

i = 3;
i = i++;

将导致未定义的行为。

如果它可以导致多个结果,我们使用术语“未定义的行为”。i但是在这里,无论评估顺序如何,最终值都是4,所以这不是真的应该称为“未指定行为”吗?

4

9 回答 9

30

短语“……i无论评估顺序如何……的最终值都是 4……”是不正确的。编译器可以发出等价的:

i = 3;
int tmp = i;
++i;
i = tmp;

或这个:

i = 3;
++i;
i = i - 1;

或这个:

i = 3;
i = i;
++i;

至于术语的定义,如果保证答案是 4,那将不是未指定或未定义的行为,而是已定义的行为。

就目前而言,根据标准(Wikipedia),这是未定义的行为,因此甚至可以免费执行此操作:

i = 3;
system("sudo rm -rf /"); // DO NOT TRY THIS AT HOME … OR AT WORK … OR ANYWHERE.
于 2011-02-11T12:17:48.380 回答
9

不,当它可以简单地导致多个算术结果时,我们不会使用术语“未定义的行为”。当行为仅限于不同的算术结果(或更一般地说,仅限于一组可预测的结果)时,它通常被称为未指定行为。

未定义的行为意味着完全不可预测和无限的后果,例如格式化计算机上的硬盘驱动器或只是让程序崩溃。并且i = i++未定义的行为。

在这种情况下,您从哪里得到i应该是 4 的想法尚不清楚。C++ 语言中绝对没有任何东西可以让你得出这样的结论。

于 2011-02-11T12:28:02.437 回答
6

在 C 和 C++ 中,两个序列点之间的任何操作的顺序完全取决于编译器,不能依赖。该标准定义了组成序列点的事物列表,从内存中这是

  1. 语句后的分号
  2. 逗号运算符
  3. 在调用函数之前评估所有函数参数
  4. && 和 || 操作数

在维基百科上查找页面,列表更完整,描述更详细。序列点是一个非常重要的概念,如果你还不知道它的含义,马上学习它会让你受益匪浅。

于 2011-02-11T12:24:57.360 回答
5

1. 不可以,根据评价顺序不同,结果会有所不同。增量和赋值之间没有求值边界,因此可以在赋值之前或之后进行增量。考虑这种行为:

load i into CX
copy CX to DX
increase DX
store DX in i
store CX in i

结果是i包含3,不是4

作为比较,在 C#中,表达式的求值和赋值之间存在求值边界,因此结果将始终3.

2. 即使没有指定确切的行为,规范也非常清楚地说明了它所涵盖的内容和未涵盖的内容。行为被指定为未定义,它不是未指定的。

于 2011-02-11T12:30:17.643 回答
4

i= 和 i++ 都是修改 i 的副作用。

i++ 并不意味着 i 仅在评估整个语句后才递增,只是表示 i 的当前值已被读取。因此,分配和增量可以以任何顺序发生。

于 2011-02-11T12:19:16.713 回答
3

这个问题很老,但似乎仍然经常被引用,因此根据 C++17 标准的变化,它值得一个新的答案。

expr.ass子条款 1 解释

...在左右操作数的值计算之后对赋值进行排序...

右操作数在左操作数之前排序。

这里的含义是右操作数的副作用在赋值之前排序,这意味着表达式没有被 [basic.exec]子条款 10中的规定处理:

如果内存位置([intro.memory])上的副作用相对于同一内存位置上的另一个副作用或使用同一内存位置中任何对象的值的值计算是无序的,并且它们不是潜在的并发([intro.multithread]),行为未定义

行为已定义,如紧随其后的示例中所述。

另请参阅:是什么让 i = i++ + 1; 在 C++17 中合法吗?

于 2019-10-20T01:17:50.393 回答
2

要回答您的问题:

  1. 我认为“未定义的行为”意味着编译器/语言实现者可以自由地做它认为最好的任何事情,并且不会导致多个结果。
  2. 因为它不是未指定的。它明确指出它的行为是undefined
于 2011-02-11T12:16:54.910 回答
0

当您可以简单地键入 i++ 时,键入 i=i++ 是不值得的。

于 2011-02-11T12:20:47.933 回答
0

我在 OCAJP 练习测试中看到了这样的问题。IntelliJ 的 IDEA 反编译器变成了这个

public static int iplus(){
    int i=0;
    return i=i++;
}

进入这个

public static int iplus() {
    int i = 0;
    byte var10000 = i;
    int var1 = i + 1;
    return var10000;
}

从模块创建 JAR,然后作为库导入并检查。 在此处输入图像描述

于 2019-06-04T17:58:09.870 回答