是while(1); C中的未定义行为?

在C ++ 11中,它是未定义的行为 ,但在C中是这样的, while(1); 是未定义的行为?

这是明确定义的行为。 在C11中增加了新的第6.8.5条款第6款

一个迭代语句,其控制表达式不是常量表达式, 156)不执行输入/输出操作,不访问易失性对象,并且不在其体内执行同步或primefaces操作,控制表达式,或(在for的情况下)声明)其表达式-3,可以由实现假定终止。 157)


157) 这是为了允许编译器转换,例如即使在无法certificate终止时也可以删除空循环。

由于循环的控制表达式是常量,编译器可能不会认为循环终止。 这适用于应该永远运行的被动程序,如操作系统。

但是对于以下循环,行为不清楚

 a = 1; while(a); 

实际上,编译器可能会或可能不会删除此循环,从而导致程序可能终止或可能不终止。 这不是真正的未定义,因为它不允许擦除你的硬盘,但它是一个避免的结构。

然而,还有另一个障碍,请考虑以下代码:

 a = 1; while(a) while(1); 

既然编译器可能假设外部循环终止,那么内部循环也应该终止,外部循环如何终止。 所以,如果你有一个非常聪明的编译器,那么一段while(1); 不应该终止的循环必须具有围绕它的所有非终止循环一直到main 。 如果你真的想要无限循环,你最好在其中读取或写入一些volatile变量。

为什么这个条款不实用

我们的编译器公司不太可能会使用这个子句,主要是因为它是一个非常语法的属性。 在中间表示(IR)中,通过恒定传播容易丢失上述示例中的常数和变量之间的差异。

该子句的目的是允许编译器编写者应用所需的转换,如下所示。 考虑一个不那么罕见的循环:

 int f(unsigned int n, int *a) { unsigned int i; int s; s = 0; for (i = 10U; i <= n; i++) { s += a[i]; } return s; } 

出于架构原因(例如硬件循环),我们希望将此代码转换为:

 int f(unsigned int n, int *a) { unsigned int i; int s; s = 0; for (i = 0; i < n-9; i++) { s += a[i+10]; } return s; } 

如果没有第6.8.5条和第6条,这是不可能的,因为如果n等于UINT_MAX ,则循环可能不会终止。 然而,对于一个人来说,这是非常清楚的,这不是本代码作者的意图。 条款6.8.5和6现在允许这种转变。 然而,对于编译器编写器来说,实现这种方式并不是很实用,因为无限循环的语法要求很难在IR上维护。

请注意, niunsigned因为signed int上的溢出会给出未定义的行为,因此可以certificate转换是正当的。 然而,除了更大的正范围外,有效代码还可以使用unsigned代码。

另一种方法

我们的方法是代码编写器必须通过例如在循环之前插入assert(n < UINT_MAX)或者像Frama-C一样保证来表达他的意图。 这样编译器就可以“certificate”终止,而不必依赖第6.8.5条款6。

PS:我正在看2011年4月12日的选秀,因为paxdiablo显然正在寻找一个不同的版本,也许他的版本更新。 在他的引用中,没有提到常量表达的元素。

在检查了C99标准草案后,我会说“不”,这不是未定义的。 我在草案中找不到任何语言提到迭代结束的要求。

描述迭代语句语义的段落全文如下:

迭代语句会导致一个称为循环体的语句重复执行,直到控制表达式比较等于0。

如果适用,我希望有任何限制,例如为C ++ 11指定的限制。 还有一个名为“约束”的部分,也没有提到任何这样的约束。

当然,实际标准可能会说别的,但我对此表示怀疑。

最简单的答案涉及§5.1.2.3p6的引用,其中陈述了符合实现的最低要求:

符合实施的最低要求是:

– 严格根据抽象机器的规则评估对易失性对象的访问。

– 在程序终止时,写入文件的所有数据应与根据抽象语义产生的程序执行结果相同。

– 交互设备的输入和输出动态应按照7.21.3的规定进行。 这些要求的目的是尽快出现无缓冲或行缓冲输出,以确保在程序等待输入之前实际出现提示消息。

这是该程序的可观察行为。

如果机器代码由于执行优化而无法产生可观察行为,则编译器不是C编译器。 在终止点,只包含这种无限循环的程序的可观察行为是什么? 这种循环可以结束的唯一方法是通过一个导致它过早结束的信号。 在SIGTERM的情况下,程序终止。 这将导致无法观察到的行为。 因此,该程序唯一有效的优化是编译器抢占系统关闭程序并生成立即结束的程序。

 /* unoptimised version */ int main() { for (;;); puts("The loop has ended"); } /* optimised version */ int main() { } 

一种可能性是引发信号并调用longjmp以使执行跳转到不同的位置。 似乎唯一可以跳转到的地方是在循环之前执行期间达到的某个地方,因此提供编译器足够智能以注意到信号被引发导致执行跳转到其他地方,它可能潜在地优化循环(和信号提升)偏离立即跳跃。

当多个线程进入等式时,有效的实现可能能够将程序的所有权从主线程转移到另一个线程,并结束主线程。 无论优化如何,程序的可观察行为仍必须是可观察的。

以下陈述出现在C11 6.8.5 Iteration statements /6

一个迭代语句,其控制表达式不是常量表达式,不执行输入/输出操作,不访问volatile对象,并且不在其主体中执行同步或primefaces操作,控制表达式,或(在for语句的情况下)它的表达式-3可以由实现假设终止。

从而while(1); 使用常量表达式,不允许实现假定它将终止。

编译器可以完全自由地删除这样的循环,表达式是非常量的,并且所有其他条件都被类似地满足,即使无法确定循环终止也是如此。