Skip to main content

Command Palette

Search for a command to run...

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

Updated
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

More from this blog

簡介 C++ 的 Type Erase (用多型和模板做 Duck Type)

起點 讓我們先從 template 出發:foo 需要一個 callback function。 template<typename Func> void foo(Func callback) { // ... callback(); } 但是這會讓編譯錯誤訊息有點模糊:假如 callback 並不是一個可以呼叫的函數指標,或者並不是一個 callable object ,那編譯器會說錯出在第四行。但是我們都希望,編譯器在呼叫函數時就幫我們指出:這不是 foo 想要的 call...

May 14, 20243 min read

帕秋莉的魔法筆記

45 posts

後端工程師。

不定時張貼一些寫扣時的筆記。