C标准中有什么(我想目前 C99 + TC1-3 C11)保证 &
并且|
不会被短路?
如果我写:
x = y & foo();
...我希望foo
始终被调用,但这是真的定义了吗?从理论上讲,除非标准另有说法,如果y
包含0
,运行时优化可能会在缺少某些不允许的内容时跳过调用。 (与|
类似,如果左侧操作数已经全部为位,则可以忽略右侧操作数。就此而言,即使x = y * foo();
<如果y
为0
,则em> 会被短路。)
不熟悉规范(我不知道),证明这样的负面是很棘手的。我可以对&
(C99中的6.5.10)和&&
(C99中的6.5.13)中的部分进行对比。在后者中,它非常清楚:
与按位二进制
&
运算符不同,&&
运算符保证从左到右的评估;在评估第一个操作数后有一个序列点。如果第一个操作数比较等于0
,则不评估第二个操作数。
...但6.5.10没有具体说明该版本的负面版本。
我觉得6.5.10 不定义一个序列点意味着foo
将始终被调用以及一个未调用的实现这一事实似乎是合情合理的。这将是非标准的。我是对的吗?
答案 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通常的算术转换是在操作数上执行的。
你有它。应用通常的算术转换需要评估两个操作数。请注意,此段落不在&&
,||
或?:
的语义描述中。