在 C++11 中是未定义行为,但在 C 中,while(1);
是否也是未定义行为?
这是一种明确定义的行为。在C11中,新增了一个6.8.5条款6。
迭代语句的控制表达式不是常量表达式156),其在执行过程中不进行输入/输出操作,不访问易变对象,并且在其语句体、控制表达式或(在for语句的情况下)表达式3中不执行同步或原子操作,实现可以假定该循环会终止157)。
157)这旨在允许编译器进行转换,例如即使无法证明终止,也可以删除空循环。
由于您循环的控制表达式是常量,编译器可能不会假定循环终止。这适用于应该永远运行的反应程序,例如操作系统。
但是对于以下循环,其行为是不清楚的。
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上维护无限循环的语法要求很难。
请注意,n
和i
必须是unsigned
,因为在signed int
上溢出会导致未定义行为,因此可以基于这个原因来证明该转换是合理的。然而,使用unsigned
除了拥有更大的正数范围之外,还有助于编写高效的代码。
另一种方法
我们的方法是,代码编写者必须通过插入像assert(n < UINT_MAX)
这样的东西或类似Frama-C的保证来表达他的意图。这样,编译器可以“证明”终止,并且不必依赖于6.8.5和6条款。
P.S:我正在查看2011年4月12日的草案,因为Paxdiablo明显正在查看不同版本,也许他的版本更新了。在他的引用中,未提及常量表达式的要素。
符合实现的最少要求是:
— 对于易失性对象的访问必须严格按照抽象机器的规则进行评估。
— 在程序终止时,写入文件的所有数据必须与根据抽象语义执行程序产生的结果完全相同。
— 交互设备的输入和输出动态行为应按照7.21.3中指定的方式进行。这些要求的目的是确保未缓冲或行缓冲输出尽可能快地出现,以确保提示消息实际上在程序等待输入之前出现。
这是程序的可观察行为。
如果机器码由于执行的优化而无法产生可观察的行为,则编译器不是C编译器。一个只包含这样一个无限循环的程序在终止点的可观察行为是什么?这样的循环唯一能够结束的方式是通过信号使其过早地结束。在SIGTERM
的情况下,程序终止。这将不会产生任何可观察的行为。因此,该程序的唯一有效优化是编译器抢先系统关闭程序并生成立即结束的程序。/* unoptimised version */
int main() {
for (;;);
puts("The loop has ended");
}
/* optimised version */
int main() { }
for(;;); printf ...
之所以没有打印任何内容,几乎肯定是由于C11 6.8.5.3/2: 省略的表达式-2将被替换为非零常量。
因此,它与for(;1;)
完全相同,由于常量表达式的限制,无法被优化消除。 - paxdiablo
for(;;)
语句被定义良好,那么while(1)
也不应该在C语言中是未定义的。请记住,无限循环的检测是一个不可判定的问题。 - Grijesh Chauhan