是同时(1); 用C未定义行为?(Is while(1); undefined behavior

2019-09-02 05:28发布

在C ++ 11是未定义行为 ,但是它在C的情况下while(1); 是未定义行为?

Answer 1:

这是明确的行为。 在C11新条款6.8.5广告6已添加

不执行任何输入/输出操作,不访问易失性的目的,并且不执行同步或在其主体的原子操作,控制表达,或(在一对的情况下的循环语句,其控制表达式不是一个常量表达式,156)声明)其表达-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。

请注意,这是至关重要的niunsigned的溢出signed int给出了不确定的行为,因此改造是合乎情理的这个原因。 高效的代码不过使用效益unsigned ,除了更大的正数范围。

另一种方法

我们的方法是,该代码作家有以表达由例如插入他的意图assert(n < UINT_MAX)循环或某些邮资-C等保证之前。 这样编译器可以“证明”终止,没有依靠第6.8.5广告6。

PS:我期待在2011年4月12日的草案paxdiablo显然是在寻找一个不同的版本,也许他的版本更新。 在他的报价中没有提及常量表达式的元素。



Answer 2:

在检查后的C99标准草案 ,我会说“不”,这不是不确定的。 我找不到在提到,迭代停止要求在草案的任何语言。

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

循环语句导致称为要被反复执行循环体语句直到控制表达式比较等于0。

我希望任何限制,如一个specififed为C ++ 11如果适用于出现在那里。 还有一个名为“约束”部分,也没有提到任何这样的限制。

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



Answer 3:

最简单的答案涉及从§5.1.2.3p6报价,其中规定一个一致的实现的最低要求:

对符合标准的实现最低要求是:

- 访问易失性对象被根据所述抽象机的规则严格评价。

- 在程序终止,写入到文件中的所有数据将是相同的结果,根据所述抽象语义程序的执行将产生。

- 交互式设备的输入和输出动态如7.21.3指定应发生。 这些要求的意图是,无缓冲或行缓冲输出尽快出现,以确保提示信息实际出现之前的程序等待输入。

这是程序的可观察行为。

如果机器代码不能产生可观察到的行为,由于进行的优化,则编译器不是C编译器。 什么是只包含这样一个无限循环,在终止点程序的观察到的行为? 这样的循环可能结束的唯一方法是通过使其提前结束的信号。 在的情况下SIGTERM ,该程序终止。 这将导致没有观察到的行为。 因此,该节目的唯一有效的优化是先发制人的编译器系统关闭程序并产生立即结束程序。

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

/* optimised version */
int main() { }

一种可能性是产生一个信号和longjmp被调用,以使执行跳转到不同的位置。 这似乎是一个能跃升到循环之前执行过程中的某个地方达成的唯一地方,所以提供的编译器有足够的智能注意到一个信号成为导致执行跳转到其他地方,它可能优化循环(和信号转上)离开赞成立即跳的。

当多个线程进入方程,有效的实现可能能够从主线程传送程序的所有权转让给不同的线程,并结束主线程。 该计划的观察行为仍必须观察到,无论最佳化的。



Answer 4:

下面的语句出现在C11 6.8.5 Iteration statements /6

循环语句,其控制表达式不是一个常量表达式,即不执行输入/输出操作,不访问易失性的目的,并且不执行同步或在其主体的原子操作,控制表达,或(在一个for语句的情况下)其表达-3,可通过终止执行被假设。

由于while(1); 使用常量表达式,实施不允许假设它会终止。

编译器可以自由地去除这样的环是完全表达被非恒定和所有其他条件都满足同样,即使它不能被明确地证明了环将终止。



文章来源: Is while(1); undefined behavior in C?