【发布时间】:2011-04-05 06:54:09
【问题描述】:
已更新,见下文!
我听说过 C++0x 允许编译器为以下 sn-p 打印“Hello”
#include <iostream>
int main() {
while(1)
;
std::cout << "Hello" << std::endl;
}
这显然与线程和优化能力有关。在我看来,这可能会让很多人感到惊讶。
有人对为什么有必要允许这样做有很好的解释吗?作为参考,最新的 C++0x 草案在6.5/5
一个循环,在 for 语句的情况下,在 for-init-statement 之外,
- 不调用库 I/O 函数,并且
- 不访问或修改易失性对象,并且
- 不执行同步操作 (1.10) 或原子操作(第 29 条)
可以假定由实现终止。 [注意:这是为了允许编译器转换 即使无法证明终止,也可以删除空循环。 ——尾注]
编辑:
This insightful article 谈到标准文本
不幸的是,没有使用“未定义的行为”一词。但是,只要标准说“编译器可能假定 P”,就暗示具有 not-P 属性的程序具有未定义的语义。
是否正确,编译器是否允许为上述程序打印“Bye”?
还有一个更有见地的thread here,它是关于对 C 的类似更改,由完成上述链接文章的 Guy 开始。在其他有用的事实中,他们提出了一个似乎也适用于 C++0x 的解决方案(更新:这将不再适用于 n3225 - 见下文!)
endless:
goto endless;
似乎不允许编译器对其进行优化,因为它不是循环,而是跳转。另一个人总结了 C++0x 和 C201X 中的提议更改
通过编写循环,程序员断言或者 循环做一些可见的行为(执行 I/O,访问 volatile 对象,或执行同步或原子操作), 或它最终会终止。如果我违反了那个假设 通过编写一个没有副作用的无限循环,我在撒谎 编译器,我的程序的行为是未定义的。 (如果我幸运的话, 编译器可能会警告我。)该语言不提供 (不再提供?)一种表达无限循环的方法 可见的行为。
3.1.2011 更新 n3225:委员会将文本移至 1.10/24 并说
实现可能假设任何线程最终都会执行以下操作之一:
- 终止,
- 调用库 I/O 函数,
- 访问或修改易失性对象,或
- 执行同步操作或原子操作。
goto 技巧将不再起作用了!
【问题讨论】:
-
while(1) { MyMysteriousFunction(); }必须在不知道那个神秘函数的定义的情况下独立编译,对吧?那么我们如何确定它是否调用了任何库 I/O 函数呢?换句话说:第一个项目符号肯定可以表述为不调用函数。 -
@Daniel:如果它可以访问函数的定义,它可以证明很多事情。有过程间优化之类的东西。
-
现在,在 C++03 中,是否允许编译器将
int x = 1; for(int i = 0; i < 10; ++i) do_something(&i); x++;更改为for(int i = 0; i < 10; ++i) do_something(&i); int x = 2;?或者可能是另一种方式,在循环之前将x初始化为2。它可以告诉do_something不关心x的值,所以它是一个非常安全的优化,ifdo_something不会导致i的值改变,这样你最终会陷入无限循环。 -
那么这是否意味着
main() { start_daemon_thread(); while(1) { sleep(1000); } }可能会立即退出,而不是在后台线程中运行我的守护进程? -
“这篇有见地的文章”假设特定行为是未定义行为,仅仅是因为没有明确的、已定义的行为。这是一个错误的假设。一般而言,当标准开放有限数量的行为时,实现必须选择其中任何一个(未指定行为)。这不必是确定性的。无所事事的循环是否终止可以说是一个布尔选择;要么有,要么没有。不允许做其他事情。
标签: c++ loops c++11 language-lawyer undefined-behavior