当前的C标准是否禁止短路`&`和`|`?

时间:2012-02-13 18:02:00

标签: c operators

C标准中有什么(我想目前 C99 + TC1-3 C11保证 &并且|不会被短路?

如果我写:

x = y & foo();

...我希望foo 始终被调用,但这是真的定义了吗?从理论上讲,除非标准另有说法,如果y包含0,运行时优化可能会在缺少某些不允许的内容时跳过调用。 (与|类似,如果左侧操作数已经全部为位,则可以忽略右侧操​​作数。就此而言,即使x = y * foo(); <如果y0,则em> 会被短路。)

不熟悉规范(我不知道),证明这样的负面是很棘手的。我可以对&(C99中的6.5.10)和&&(C99中的6.5.13)中的部分进行对比。在后者中,它非常清楚:

  

与按位二进制&运算符不同,&&运算符保证从左到右的评估;在评估第一个操作数后有一个序列点。如果第一个操作数比较等于0,则不评估第二个操作数。

...但6.5.10没有具体说明该版本的负面版本。

我觉得6.5.10 定义一个序列点意味着foo将始终被调用以及一个未调用的实现这一事实似乎是合情合理的。这将是非标准的。我是对的吗?

4 个答案:

答案 0 :(得分:19)

  

我认为6.5.10没有定义一个序列点来表示foo总是被调用而且没有调用它的实现是非标准的,这似乎是合理的。我是对的吗?

是和否。实际上,不会调用foo的实现将是非标准的。但是,它与序列点没有任何关系。

此处适用的段落为5.1.2.3/3:

  

在抽象机器中,所有表达式都按语义指定进行评估。一个   实际实现不需要评估表达式的一部分,如果它可以推导出它   不使用值,不产生任何副作用(包括由...引起的任何副作用)   调用函数或访问volatile对象。)

答案 1 :(得分:14)

在这种情况下,序列点与它无关。 C标准定义了逻辑机器的行为方式,并要求实现行为,好像它遵循该行为;任何优化对于程序的行为都必须是透明的。

C语言中唯一定义为不评估(全部)操作数的运算符是?:&&||sizeof。实现可以短路|&的唯一方法是,如果它确定(1)单个操作数的值足以知道结果,或者至少知道结果是否为当结果仅被用作真值时为零或非零,以及(2)另一个操作数没有副作用,因此不评估它的行为是,就像那样评价。

通过函数调用,编译器不太可能确定它没有副作用,除非它是static或标记了编译器特定属性,如gcc的__attribute__((const))

编辑:来自C99,5.1.2.3:

  

在抽象机器中,所有表达式都按语义指定的方式进行计算。实际实现不需要评估表达式的一部分,如果它可以推断出它的值没有被使用并且没有产生所需的副作用(包括由调用函数或访问易失性对象引起的任何副作用)。

然后明确记录不评估其操作数的运算符。

答案 2 :(得分:5)

如果实现可以确定foo函数调用没有改变程序的可观察bevahior,则不需要评估foo调用。

  

(C11,5.1.2.3p4)“在抽象机器中,所有表达式都按语义指定进行评估。如果表达式可以推断出它的值没有被使用,那么实际的实现不需要计算表达式的一部分。产生了所需的副作用(包括通过调用函数或访问易失性对象引起的任何副作用)。“

在C11中增加并澄清了可观察行为的概念:

  

(C11,5.1.2.3p6)“符合要求的实施的最低要求是:    - 严格根据摘要规则评估对易失性对象的访问   机。    - 程序终止时,写入文件的所有数据应与结果相同   根据抽象语义执行程序会产生。    - 交互设备的输入和输出动态应按照规定进行   7.21.3。这些要求的目的是无缓冲或行缓冲输出   尽快出现,以确保提示消息实际出现在之前   等待输入的程序。   这是该程序的可观察行为。“

答案 3 :(得分:0)

  

§6.5.10按位AND运算符

     

...

     

<强>语义

     

3通常的算术转换是在操作数上执行的。

你有它。应用通常的算术转换需要评估两个操作数。请注意,此段落不在&&||?:的语义描述中。