在C++中循环为

for(;;) {}

是UB,而不是C?

据表示,在https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2023/p2809r0.html人中,有good reasons人这样做.有什么简单的例子可以清楚地说明这一点吗?

推荐答案

原因仅仅是优化.

如果编译器可以假定所有循环都终止而没有副作用,则不必证明这一点.

如果允许非终止循环,编译器只有在证明其可终止性的情况下才被允许执行某些优化,这在一般情况下是不可能的,因此它将变成模式识别游戏.有什么好处呢?

The underlying issue is that non-termination is a kind of side-effect on itself.当且仅当环路终止时,才能观察到在环路终止后肯定会发生的任何可观察到的效果,即使环路没有任何影响.

当然,对于if(expr) return;也可以进行完全相同的推理,编译器不允许将if后面的东西移到if之前,除非它能证明expr是假的.但是if是基本的控制流机制,而非终止循环不是(IMHO).

请使用以下代码.

int collatz_conjecture(int i){
    while(i!=1){
        if ((i%2)==0)
            i/=2;
        else
            i=i*3+1;
    }
    return i;
}

int main(){
    collatz_conjecture(10);
    
    return 5;
}

对于臭氧,GCC将其汇编为:

collatz_conjecture(int):
        mov     eax, 1
        ret
main:
        mov     eax, 5
        ret

那么,编译器是否证明了Collatz猜想,以确定它应该为所有数字返回1?当然不是,这只是终止假设允许的优化之一(以及UB可能发生的地方).循环可以终止的唯一方法是IF i==1,因此它可以在循环之后假定为i==1,并使用它进行进一步的优化->;函数总是返回1,因此可以简化为它.

更有用的例子可以是交错复制.如果你有

loop A
loop B

允许编译器在不知道A终止的情况下对它们进行交织.许多矢量化操作都依赖于这一假设.

同样,在循环假定循环将终止之前,对一些独立的循环后操作进行重新排序.

编辑:我指的是科拉茨猜想,不是戈德巴赫……

C++相关问答推荐

理解没有返回语句的递归C函数的行为

错误:在.h程序中重新定义 struct

GCC不警告隐式指针到整数转换'

C编译器是否遵循restrict的正式定义?

如何调试LD_PRELOAD库中的构造函数?

为什么我一直收到分段错误?

如何使用指向 struct 数组的指针并访问数组中特定索引处的 struct

有什么方法可以将字符串与我们 Select 的子字符串分开吗?喜欢:SIN(LOG(10))

不同出处的指针可以相等吗?

在C中包装两个数组?

指向不同类型的指针是否与公共初始序列规则匹配?

将回调/基于事件的C API转换为非回调API

为什么电路板被循环删除?

如何在不读取整个字符串的情况下删除UTF8字符串的尾随空格以提高性能?

强制GCC始终加载常量(即只读),即使启用了优化

令人困惑的返回和 scanf 问题相关

const struct 成员的 typedef 中的灵活数组大小

C 中从 Unix 纪元时间转换的损坏

比 * 更快的乘法

`void foo(int a[static 0]);` 有效吗?