關於 Undefined Behavior 這件事...
本篇文章我在以前的 Blogspot 有寫過,不過沒什麼點閱率 QAQ
現在好不容易這裡有一些觀看
再加上前陣子也有看到 Undefined Behavior 寫法被討論
我就把舊文章移植過來,順便把語病、說法做個修正
前幾天在看FB的文章時,看到了一篇貼文
第八題不會有太大的問題 6*6*6=216
第九題,答案是多少?
那篇貼文討論蠻熱烈的~(可是這讓我有點擔心……)
一般人第一個直覺是把++n算完後代入x,就會變成6*6*6=216,可是沒有在選項內……?
那就來執行一次看看吧
統一以這份 Code 執行
1 |
|
用兩個較主流的編譯器,GNU G++ 跟 Clang(MSVC++ 表示:…)
GNU G++
編譯參數:g++ -Wall -O2 -std=c++14 a.cpp
輸出結果 392
那第九題答案就是 (D),真相大白
.
.
.
.
.
.
答案真的那麼簡單?來看看 Clang
結果吧!
Clang
編譯參數:clang -Wall -O2 -std=c++14 a.cpp
等一下!竟然是 336?
這個就是所謂的 UB(Undefined Behavior)未定義行為
照理說,同個語言,不同編譯器,執行結果一定要一樣
這個跟運算的優先順序有點關係,每個運算子在 C/C++ 的優先順序
這有點像是遊戲規則,大規則方向有訂好
但是有些規則並沒有規定誰先誰後
自己編譯器就自己規定,反正只要照著大方向就好
像是 GNU G++ 的結果:
第一個數字 ++N 確實變成 $6$
乘法優先運算子低
把 $N$ 排入等待運算
第二個數字又把 $N$ 加上 $1$
第一個數字被連帶影響
第一個跟第二個數字相乘就變成 $7 \times 7 = 49$
把 $49$ 排入等待運算
第三個數字又把 $N$ 加上 $1$
$N$ 變成 $8$
$49 \times 8 = 392$
以上為對 GNU G++ 的個人見解
Clang 結果:
第一個數字 ++N 確實變成 $6$
乘法優先運算子低
把 $6$ 排入等待運算
第二個數字又把 $N$ 加上 $1$
第一個跟第二個數字相乘就變成 $6 \times 7 = 42$
把 $42$ 排入等待運算
第三個數字又把 $N$ 加上 $1$
$N$ 變成 $8$
$42 \times 8 = 336$
以上為對 Clang 的個人見解
以上結果其實沒有違反「運算子的優先順序」
但!這是未定義行為,真的不需要對他細節作法作討論
程式編譯經過優化後,也有可能自動幫你修改過程
未定義行為結果就可能會因此不同
結論
聽網路上說,大學教授其實出蠻多這種題目的(還好我的科系沒有考這種東西),討論這種題目基本上沒有什麼意義,錯誤的定義上討論出的結果,也只會是錯誤的。
不過我也有去找教授討論過,他上課也說過,同一個運算式,不要使用「遞增遞減運算子(Ex.i++、++i、i–、–i)」超過兩個以上,不然會發生一些不可預期的錯誤。
UB(Undefined Behavior)的例子還有很多,發生的情況不一定是執行結果不一樣
希望不要再出這種 Code 殘害學生,也不需要去討論這種東西
只要告訴他:「這是 UB(Undefined Behavior)未定義行為,不需要去多做任何討論。」