Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/c/70.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C 是否允许编译器消除无限循环?_C_Optimization_Compiler Construction_Standards_Infinite Loop - Fatal编程技术网

C 是否允许编译器消除无限循环?

C 是否允许编译器消除无限循环?,c,optimization,compiler-construction,standards,infinite-loop,C,Optimization,Compiler Construction,Standards,Infinite Loop,优化编译器可以删除无限循环吗?它不会改变任何数据,比如 while(1) /* noop */; 通过分析数据流图编译器可以得出,这样的循环是“死代码”,没有任何副作用 C90/C99标准是否禁止删除无限循环 C90或C99标准是否允许编译器删除此类循环 Upd:“微软C 6.0版基本上完成了这一优化。”参见caf的链接 label: goto label; return 0; 将转化为 return 0; 在编写守护进程时,它们是必需的。为什么要称它们为死代码?循环不是死代码,它基

优化编译器可以删除无限循环吗?它不会改变任何数据,比如

while(1) 
  /* noop */;
通过分析数据流图编译器可以得出,这样的循环是“死代码”,没有任何副作用

C90/C99标准是否禁止删除无限循环

C90或C99标准是否允许编译器删除此类循环

Upd:“微软C 6.0版基本上完成了这一优化。”参见caf的链接

label: goto label;
return 0;
将转化为

return 0;

在编写守护进程时,它们是必需的。为什么要称它们为死代码?

循环不是死代码,它基本上阻止程序到达它之后的任何地方。如果删除循环,则不会发生这种情况,因此编译器无法删除循环

它可能会用一条依赖于平台的空闲指令来代替它,以向处理器发出信号,表示线程将不再执行任何操作


编译器所能做的就是删除循环后的任何代码,因为它是不可访问的,并且永远不会执行。

没有办法普遍检测无限循环:请参阅。因此,任何编译器所能做的最好的事情就是做一个合理的猜测——例如OP中提到的显而易见的情况


但为什么这是可取的呢?我可以看到发出警告,但仍然允许该行为,但删除循环并不是“优化”——它会改变程序的行为

这在
comp.lang.c
(例如)上已经讨论过很多次,据我所知,没有任何一致的结果。

我相信新的标准明确规定,如果一段代码不访问任何可变变量,则执行I/O,任何其他不依赖于第一部分中计算的任何内容的代码可以在其之前任意排序。如果无限循环不执行任何I/O,也不计算稍后在程序中使用的任何值,编译器可以简单地推迟循环的执行,直到所有其他操作完成。

澄清了这个问题的答案,在C11标准草案部分
6.8.5
迭代语句中添加了以下段落:

一种迭代语句,其控制表达式不是常量 不执行输入/输出操作的表达式(156)不 访问易失性对象,不执行同步或原子 其主体中的操作、控制表达式,或(在 对于语句),其表达式-3可由实现假设 终止(第157段)

脚注
157
说:

这是为了允许编译器转换,例如删除空循环,即使在 无法证明终止

那么你的具体例子是:

while(1) 
  /* noop */;
因为控制表达式是一个常量表达式,所以这对于优化来说是不公平的

作为UB的无限循环

那么,为什么编译器可以优化无限循环呢除了上面提供的例外,Hans Boehm提供了一个理由,让无限循环成为未定义的行为:,下面的引文对涉及的问题给出了一个很好的感觉:

正如N1509正确指出的,当前草案基本上给出了 6.8.5p6中无限循环的未定义行为。一个重要的问题 这样做是因为它允许代码跨潜在的 非终止循环。例如,假设我们有以下循环, 其中count和count2是全局变量(或有其地址 取),p是一个局部变量,其地址尚未取:

for (p = q; p != 0; p = p -> next) {
    ++count;
}
for (p = q; p != 0; p = p -> next) {
    ++count2;
}
这两个循环是否可以合并并替换为以下循环

for (p = q; p != 0; p = p -> next) {
        ++count;
        ++count2;
}
如果没有6.8.5p6中针对无限循环的特殊豁免,则 不允许:如果第一个循环没有终止,因为q 指向循环列表,原始列表从不写入count2。因此 它可以与另一个访问或访问的线程并行运行 更新计数2。转换后的版本不再安全 尽管有无限循环,它仍然访问count2。因此 转换可能会引入数据竞争

在这种情况下,编译器不太可能 证明循环终止;它必须理解q点 一个非循环列表,我相信这是大多数人无法做到的 主流编译器,通常没有整个程序是不可能的 信息

C99

由于C99没有这种划分,我们将参考
5.1.2.3
节中的“好像”规则,该规则基本上说编译器只需模拟程序的可观察行为,要求如下:

一致性实施的最低要求是:

  • 在序列点,易失性对象是稳定的,因为以前的访问是不稳定的 尚未完成和后续访问
  • 程序终止时,写入文件的所有数据应与 根据抽象语义执行程序会产生错误
  • 交互设备的输入和输出动态应按照 7.19.3. 这些要求的目的是实现无缓冲或线路缓冲输出 尽快显示,以确保提示消息在 等待输入的程序
对此的严格解读似乎允许实现优化无限循环。我们当然可以提出这样的场景:优化无限循环将导致可观察行为的变化:

while(1) ;
printf( "hello world\n" ) ;
许多人认为,影响过程的终止也是可观察的行为,这一立场是:

编译器在如何实现C程序方面有相当大的自由度,但其输出必须具有与解释b时程序相同的外部可见行为