Ton*_*ion 24 c infinite-loop undefined-behavior
在C++ 11中,它是未定义的行为,但在C中的情况while(1);
是未定义的行为吗?
Bry*_*ier 23
这是明确定义的行为.在C11中增加了新的第6.8.5条款第6款
一个迭代语句,其控制表达式不是常量表达式,156)不执行输入/输出操作,不访问易失性对象,并且不在其体内执行同步或原子操作,控制表达式,或(在for的情况下)声明)其表达式-3,可以由实现假定终止.157)
157)这是为了允许编译器转换,例如即使在无法证明终止时也删除空循环.
由于循环的控制表达式是常量,编译器可能不会认为循环终止.这适用于应该永远运行的被动程序,如操作系统.
但是对于以下循环,行为不清楚
a = 1; while(a);
Run Code Online (Sandbox Code Playgroud)
实际上,编译器可能会或可能不会删除此循环,从而导致程序可能终止或可能不终止.这不是真正的未定义,因为它不允许擦除你的硬盘,但它是一个避免的结构.
然而,还有另一个障碍,请考虑以下代码:
a = 1; while(a) while(1);
Run Code Online (Sandbox Code Playgroud)
现在,由于编译器可能假设外部循环终止,内部循环也应该终止,外部循环如何终止.因此,如果你有一个非常聪明的编译器,那么一个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;
}
Run Code Online (Sandbox Code Playgroud)
出于架构原因(例如硬件循环),我们希望将此代码转换为:
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;
}
Run Code Online (Sandbox Code Playgroud)
如果没有第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.
PS:我正在看2011年4月12日的选秀,因为paxdiablo显然正在寻找一个不同的版本,也许他的版本更新.在他的引文中,没有提到常量表达的元素.