C++ 有關迴圈的未定義行為?

·

1 min read

TL;DR

沒有 Side Effect 的無窮迴圈是未定義行為。

起源

Source: https://twitter.com/PR0GRAMMERHUM0R/status/1623366075357270019/photo/1

可以先猜猜這段 code 會有什麼輸出(x86_64 clang 15.0.0 -O1)

#include <iostream>

int main() {
    while (1);
    return 0;
}

void unreachable() {
    std::cout << "why?" << std::endl;
}

結果竟然會印出:(Compiler Explorer

why?

為什麼

參考一些文章,大致上是說沒有 side effect 的迴圈可能會被一些 compiler 決定砍掉,例如:

void collatz(unsigned int n) {
    while (n > 1) {
        if (n % 2 == 0) {
            n = n / 2;
        } else {
            n = 3 * n + 1;
        }
    }
}

會被編成 (Compiler Explorer

collatz(unsigned int):                            # @collatz(unsigned int)
        ret

這並不是因為 compiler 幫你在 unsigned int 範圍證明了 Collatz Conjecture ,而是他直接假定會跑完迴圈,並且由於沒有 side effect ,所以直接剪掉。

一開始的起源,則是被編成

main:                                   # @main
unreachable():                       # @unreachable()
        push    rbx
        mov     rbx, qword ptr [rip + std::cout@GOTPCREL]
        lea     rsi, [rip + .L.str]
        mov     edx, 4
        ...

進入 main 時,這直接進到 unreachable 。

額外的一些小細節

  • 用 clang 11 編不會出現這件事:
main:                                   # @main
.LBB1_1:                                # =>This Inner Loop Header: Depth=1
        jmp     .LBB1_1
  • 用 gcc 編不會出現這件事:
main:
.L2:
        jmp     .L2
  • 把 -O1 拿掉不會出現這件事:
main:                                   # @main
        push    rbp
        mov     rbp, rsp
        mov     dword ptr [rbp - 4], 0
.LBB1_1:                                # =>This Inner Loop Header: Depth=1
        jmp     .LBB1_1
  • x86/x64 msvc 19 不會出現這件事:
_main   PROC                                      ; COMDAT
$LL2@main:
        jmp     SHORT $LL2@main
_main   ENDP