C中的序列点和副作用

时间:2013-07-04 15:15:53

标签: c undefined-behavior sequence-points

在这个C-FAQ中,它是关于sequence point的;

  

标准规定;
  在上一个和下一个序列点之间,对象的存储值最多只能通过表达式的计算修改一次。此外,只能访问先前值以确定要存储的值。

在例子中

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

从声明的第一句可以清楚地看出,这些例子是未定义行为的结果。
在解释声明的第二句时,据说;

  

第二句话说:如果一个对象被写入一个完整的表达式,那么在同一个表达式中对它的任何和所有访问都必须直接参与计算要写入的值。此规则有效地将法律表达式约束为在修改之前明显存在访问的表达式。例如,旧备用

 i = i + 1 
  允许

,因为i的访问权限用于确定我的最终价值。示例

a[i] = i++
  

是不允许的,因为i的一个访问(a [i]中的一个)与最终存储在i中的值无关(在i ++中发生),所以&# 39;没有好的方法来定义。

我的问题是;
1.它是什么意思,如果一个对象被写入一个完整的表达式,那么在同一个表达式中对它的任何和所有访问都必须直接参与计算要写入的值。

2.它是什么意思,示例a[i] = i++ 是不被禁止的,因为i的一个访问(a [i]中的一个)与最终存储在i中的值无关(在i ++中发生)
有人可以用一些简单的方法解释它吗?

3 个答案:

答案 0 :(得分:6)

  

我的问题是;   1.它是什么意思,如果一个对象被写入一个完整的表达式,那么在同一个表达式中对它的任何和所有访问都必须   直接参与计算要写入的值。?

使用i++之类的子表达式,写入i。此外,赋值是一个表达式,因此在i = 2中,i被写入。 a = b是一个表达式可能不是很明显,但确实如此。这就是为什么你可以做a = b = c这个好的,if (a = b)不太好的原因。

所以它的含义是,如果你写i=,或者前后增加,那么对i的任何访问都必须作为新计算的一部分。我的价值。但是,这一点非常重要,计算前后增量所涉及的唯一内容就是语句开头的i 的值

  

2.它是什么意思,例子a [i] = i ++是不允许的,因为其中一个访问    i(a [i]中的那个)与最终的值无关   存储在i中(在i ++中发生)

准确地说是什么。当您在i中访问a[i]时,它不属于i的{​​{1}}新值计算的一部分。

  

有人可以用一些简单的方法解释它吗?

简单方法:不要在表达式中使用前置或后置增量。总是在声明中自己使用它们。如果你真的必须这样做,请不要在整个语句中的其他地方使用相同的变量。

答案 1 :(得分:2)

这种解释很奇怪,我无法理解它。

真正的解释是表达式i++具有副作用(递增i),可以在评估此特定i之后随时应用。

由于序列点之外,C语言不保证评估顺序或应用后递增的时间(可能出于性能原因),在第二个示例中可能发生三件事(我们假设行前有i = 5):

  • 首先评估最左边的ia[i]中的一个),以计算[i]的存储地址。然后评估最右边的i,然后应用后增量:该行执行a[5] = 5; i=6;
  • 首先评估最右边的i,然后评估最左边的i,然后在完成所有操作时应用i后增量:在这种情况下,效果与上述情况相同
  • 首先评估最右边的i,立即应用后增量,然后评估最左边的a[6] = 5; i=6以计算用于存储的[i]的地址。这次效果为{{1}}。

结果的选择不仅取决于编译器的选择,还取决于编译器的设置:Microsoft Visual C ++可以根据您是在Debug还是Release中编译来提供不同的结果。

答案 2 :(得分:1)

最后,关于这一点我得到了explanation。阅读完之后FAQ 我总结了;

1.最后一句

  

此外,只能访问先前值以确定要存储的值

会是这样的;

  

此外,只能访问对象的先前值以确定已修改/新值(同一对象的 ) )存储。

通过示例清楚

 int i = 1, j, a[5];    
 i = i + 1;
 j = i + 1;
 a[i] = i; 

如果表达式i = i + 1 1(在RHS中)的先前值(此处为i)被访问以确定要存储的i的值,并且这就是声明

  

如果一个对象被写入一个完整的表达式,那么在同一个表达式中对它的任何和所有访问都必须直接参与计算要写入的值   。

说。
 在j = i + 1a[i] = i的情况下,i的访问值为仅值 之前值为no,其中i在这些语句中被修改。

2.第二个问题可以解释为:
如果是a[i] = i++a[i++] = i,则为上述陈述的第一句

  

在上一个和下一个序列点之间,对象的存储值最多只能通过表达式的评估修改一次。

失败,因为i 仅在两个连续序列点之间修改了一次。这就是我们需要第二句话的原因 C中不允许这两个示例,因为i的先前值访问了两次,即i++本身访问要修改的表达式中i先前值它因此i先前值/值的其他访问是不必要的,因为它不被访问以确定要存储的修改值。