關於 Undefined Behavior 這件事...

本篇文章我在以前的 Blogspot 有寫過,不過沒什麼點閱率 QAQ
現在好不容易這裡有一些觀看
再加上前陣子也有看到 Undefined Behavior 寫法被討論
我就把舊文章移植過來,順便把語病、說法做個修正

前幾天在看FB的文章時,看到了一篇貼文

第八題不會有太大的問題 6*6*6=216
第九題,答案是多少?

那篇貼文討論蠻熱烈的~(可是這讓我有點擔心……)
一般人第一個直覺是把++n算完後代入x,就會變成6*6*6=216,可是沒有在選項內……?
那就來執行一次看看吧

統一以這份 Code 執行

1
2
3
4
5
6
7
8
9
#include<cstdio>
using namespace std;
#define CUBE(x) x*x*x
int main()
{
int n = 5;
printf("%d\n", CUBE(++n));
return 0;
}

用兩個較主流的編譯器,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)未定義行為,不需要去多做任何討論。」


關於 Undefined Behavior 這件事...
https://blog.yangjerry.tw/2020/06/02/undefined-behavior-in-c/
作者
Jerry Yang
發布於
2020年6月2日
許可協議