所以我在quora文章中看到了这段代码来交换两个数字。
a = a + b - (b = a);
我试过了,效果很好。但是由于b = a
括号中的 is 不应该将 b 值分配给 a first 的值吗?而整件事情应该变成a + a - a
一个来保值吗?
我试过a = b + (b = a);
了a = 5 b = 10
,最后我得到了 = 10。看到这里我猜它评估为a = a + a
为什么会出现这种异常?
所以我在quora文章中看到了这段代码来交换两个数字。
a = a + b - (b = a);
我试过了,效果很好。但是由于b = a
括号中的 is 不应该将 b 值分配给 a first 的值吗?而整件事情应该变成a + a - a
一个来保值吗?
我试过a = b + (b = a);
了a = 5 b = 10
,最后我得到了 = 10。看到这里我猜它评估为a = a + a
为什么会出现这种异常?
这是未定义的行为,因为6.5.2
来自 C99 draft standard
which 的部分:
在前一个序列点和下一个序列点之间,对象的存储值最多只能通过表达式的评估修改一次。72) 此外,应只读先前的值以确定要存储的值
在这种情况下,我们正在修改b
并使用它的值来确定 的结果a
,标准给出了以下未定义的示例:
i = ++i + 1;
a[i++] = i;
至少在启动警告gcc
会警告问题,使用-W -Wall
我收到以下警告:
warning: operation on ‘b’ may be undefined [-Wsequence-point]
优先级确定了哪些操作数由哪些运算符链接。它没有建立评估顺序。
赋值运算符的优先级很低。结果,没有括号,表达式
a = a + b - b = a
将被解析为:
(a) = (a + b - b) = (a)
这将导致错误,因为 (a + b - b) 不是左值。因此,括号是必需的,以便对两个操作数b
和a
赋值运算符进行分组,如原始语句中所示:
a = a + b - (b = a)
但是所有括号强加的是分组,而不是评估顺序。
您可以确定的是,无论何时(b = a)
评估:
a
b
将被分配 的值a
。但是,编译器之间无法预测何时会发生这种情况。标准是明确的:在复杂表达式中,子表达式的求值顺序和副作用发生的顺序是未指定的,即取决于编译器。该标准对评估子表达式的顺序没有任何要求。
更一般地说,在 C 语言中,如果您在表达式中的其他地方使用该变量时修改了该变量的值,那么您得到的结果将是未定义的,这意味着任何事情都可能发生。你绝对不能依赖表达式a = a + b - (b = a)
,因为它既修改了表达式的值,b
又使用了b
表达式中其他地方的值。
因此,表达式引发了未指定的行为(依赖于特定的评估顺序)和未定义的行为(修改变量的值,同时在表达式的其他地方使用它)。当您想到它时,这是一个令人印象深刻的壮举!
编辑:上述情况适用于 C99。最新标准 C11 明确将此行为称为未定义:“如果标量对象上的副作用相对于同一标量对象上的不同副作用或使用相同标量对象的值的值计算是无序的,则该行为是未定义的。如果表达式的子表达式有多个允许的排序,则如果在任何排序中出现这种未排序的副作用,则行为未定义。(6.5.2)。标准草案免费提供。
如评论中所述,这是未定义的行为。代码是从左到右直接读取的,而不是使用 PEMDAS。