C++ 有關迴圈的未定義行為?
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