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上维护.

请注意,这是必要的niunsigned溢流上signed int给出了不确定的行为,因此改造是合乎情理的这个原因.然而unsigned,除了更大的正范围外,高效代码也可以从使用中受益.

另一种方法

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

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


unw*_*ind 5

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

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

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

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

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