C++26:无限循环不再是未定义行为
C++26: Trivial infinite loops are no longer undefined behaviour
在 C++26 之前,一个看似无害的 while (true); 循环竟然属于未定义行为。编译器如 Clang 曾利用这一规则将其优化掉,导致程序意外执行后续代码,这在嵌入式系统中可能引发严重的安全漏洞。C++26 通过提案 P2809R3 修正了这一缺陷,将“平凡无限循环”定义为明确行为,并引入 std::this_thread::yield() 机制。这一改变不仅消除了与 C 语言的不必要差异,也修复了真实场景中的代码隐患,让开发者不再担心编译器“自作聪明”地移除关键逻辑。
当优化器移除循环时,执行流程会落入链接器放置的后续代码中,就像本文开头的‘Hello world!’示例所展示的那样。
HN 评论区
273- wahern
> 当两个条件都满足时,循环体将被替换为对 std::this_thread::yield() 的调用。这赋予了循环此前所缺乏的前进语义(forward-progress semantics)。
这正是 Linus 和许多人所不喜欢的 C++ 中“隐藏代码”弊端的极致体现。对于构造函数和析构函数来说,这在某种程度上是不可避免的,也并非那么随机,不过 Rust 在限制非本地代码(non-local code)的爆炸半径方面做得更好,至少在 drop 的情况下是这样。
如果他们不想采纳 C11 的规则,C++ 委员会本应探索一种要求编译器对琐碎循环(无论是按 C11 定义还是其他方式定义)发出诊断信息或错误的规则,强制程序员显式插入 ::yield 或类似操作。这样就没有隐藏代码,编译器做出惊人行为的机会也更少。
C 委员会一直在严格列举标准中的未定义行为(UB)案例,并逐个解决,通常是通过要求诊断信息、报错,或将其转变为实现定义行为。但插入此类代码是难以想象的。
- JoshTriplett
> 当两个条件都满足时,循环体将被替换为对 std::this_thread::yield() 的调用。
此处请插入尖叫。
一个没有任何库调用的无限循环,竟然插入了一个系统调用。这是一个可怕的、等待发生的惊喜。
“前进保证(forward progress guarantee)”的整个概念都崩塌了。无限循环应该编译成无限循环。不多不少,仅此而已。
- omoikane
> 循环必须是一个字面上空的迭代语句——意味着其循环体实际上是空的
这似乎表明循环体不能是 "continue"。事实上,我刚才尝试了 -std=c++26,用 ";" 确实得到了承诺的无限循环,但 "continue" 又恢复了未定义行为:
- "while(true);" -> https://godbolt.org/z/T65o51crx
- "while(true) continue;" -> https://godbolt.org/z/Pj9raEcnP
这很不幸,因为我知道有一份风格指南更倾向于使用 "continue" 而不是单个分号。我想以后所有的代码都会改成 "while(true) {}" 了。
https://google.github.io/styleguide/cppguide.html#Formatting...
- ameliaquining
这篇文章最不幸的是,根本没有解释为什么一开始有人希望无限循环是未定义行为(UB)。我找到了这个解释:https://www.open-std.org/jtc1/sc22/wg14/www/docs/n1528.htm
- peterus
在微控制器编程中,无限 while(1) 循环有合法的用例(与普遍看法相反,似乎确实如此)。stm32 的自动生成 HAL 代码在错误处理中使用它,而且它们支持 C++,所以我很惊讶这竟然是未定义行为。
我只在错误处理中使用它,当然在功耗敏感的应用中使用它来等待/停滞是个坏主意,这种情况下应该使用中断唤醒。
顺便说一句,我喜欢在我的错误处理程序中包含一个软件断点。这使得调试更容易,而无需浪费硬件断点(微控制器的硬件断点在物理上是有限的):
__BKPT();
while (1)
;
- Panzerschrek
我看不到它有什么用处。编写这样的循环几乎总是一个错误。它存在的唯一原因是在非常底层的代码中什么都不做,但对于这种情况,使用像用汇编编写的外部函数之类的东西完全没问题,根本不需要修改 C++ 标准。这甚至让事情变得更难,因为它用几乎没有任何好处的代价让标准变得复杂。
- Aardwolf
> 实现可以假设任何线程最终都会执行以下操作之一:终止、调用库 I/O 函数、访问易失性 glvalue,或执行同步或原子操作
为什么需要这条规则?我可以让我的 for 循环试图解决停机问题,它也永远不会结束,从而绕过那条规则。
- ErikCorry
> [C 规则被 C++ 拒绝是因为它] 可能会抑制有用的优化
我很好奇,这些优化是否属于那种我觉得非常有用、以至于我愿意为此付出这种烦人新行为代价的类型?
或者,它们仅仅是编译器编写者觉得有用的那些优化,而这些人正深陷于一场与竞争对手团队之间持续多年的、定义职业生涯的“尿尿比赛”(pissing contest)中?
别误会,我自己也参与过一场与竞争对手团队之间持续多年的、定义职业生涯的“尿尿比赛”。这很有趣。但别自欺欺人,说这是为了用户好。