为什么在C语言中"f = f++"是不安全的?

9

我从这个网站上了解到了“副作用”的概念:

但我仍然不明白为什么f = f++被认为是不安全的?

有人能解释一下吗?


重复(虽然如果您不知道答案,这并不是很明显)https://dev59.com/dErSa4cB1Zd3GeqPTyXF - Martin Beckett
1
还有一个问题,为什么"++i++"是非法的,但无法搜索到相关信息! - Martin Beckett
3
此问题明确询问为什么这个结构不安全。您所提到的那个问题具有无效的代码,是因为这个结构不安全。两个完全不同但都是有效问题。 - joshperry
是的,这就是为什么我没有投票关闭的原因 - 但读一下另一个帖子也很值得。 - Martin Beckett
5个回答

16

问题出在序列点上。这个语句中有两个操作没有序列点,因此语句的执行顺序没有定义,赋值先发生还是自增先发生?

并不是说这种情况不安全,只是没有定义,这意味着不同的实现可能会得到不同的结果,或者格式化您的硬盘......


2
我认为大多数人会认为格式化驱动器是 f = f++ 的一个不安全的后果。虽然我认为大多数人所说的 '不安全' 并不是指它可能会破坏什么,而是说你不能依赖它会做什么。 - Michael Burr
C和C++标准在详细说明序列点执行顺序的部分中明确使用“未定义行为”这一术语。我只是夸张地强调了不安全和未定义之间的差异。 - joshperry
实际上它并没有,它只是指定为未定义。想想NULL和0之间的区别(显然不在C ++中)。0是一个值,NULL是缺少值。未定义意味着任何结果都可能发生;是的,包括不安全的事情,但不一定如此。 - joshperry

4

在C语言中,同一条语句中使用xx++(或++x)是未定义行为。编译器可以任意选择:先在赋值之前还是之后增加x的值。以Ólafur的代码为例,这可能导致f == 5f == 6,具体取决于你的编译器。


4
您提供的链接中的文章给出了答案。 "C 几乎不承诺单个表达式中的副作用会按可预测顺序发生。" 这意味着您不知道 = 和 ++ 的执行顺序。它取决于编译器。
如果您从该文章的链接转到同一站点上关于序列点的文章,您将看到编译器可以优化何时以及何时将值从寄存器写回变量。

还有一个需要澄清的地方——这不仅仅取决于编译器。编译器所能做的可能根本没有意义,甚至可以达到同一语句的两个实例(具有相同的 f 初始值)产生完全不同结果的程度。 - Michael Burr

1

来自标准的内容

6.5(2)如果对标量对象的副作用与同一标量对象上的不同副作用或使用相同标量对象的值计算无序,则行为未定义。如果表达式的子表达式有多个允许的排序方式,并且在任何排序中发生了这样的无序副作用,则行为未定义。74)

74)本段使语句表达式未定义,例如

             i = ++i + 1;
             a[i++] = i;

但允许

             i = i + 1;
             a[i] = i;

0

在这方面,我支持Arthur的答案。虽然后置递增运算符即f++的实现很令人困惑,但它并不被认为是不安全的。你应该先了解编译器如何解释它,即它是否会在遇到语句终止符(;)后递增f,还是在使用f的值之后立即递增。


1
这是在 C 标准的意义上未定义的。这并不是“你必须知道编译器做了什么”的情况。编译器可能会执行任何操作,包括给 f 赋一个与您认为它必须生成的原子指令排序中的任何一个都不对应的值。 - Pascal Cuoq

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接