今年投入求職大軍,抱著累積經驗心態參加 MTK 筆試。自詡熟悉 C++ 進階特性(模板、STL、Boost),卻在基礎題上栽了大跟頭。這篇記錄第一道讓我「體無完膚」的題目——看似簡單的輸出順序問題,實則暗藏 C++ 求值順序與序列點的深坑。
int a = 10, b = 6;
cout << a + b << " " << a++ << " " << b++;
直覺答案:16 10 6(先算 a+b=16,再印 a++=10, b++=6)。
實際輸出(GCC 4.5):18 10 6
為釐清真相,設計模擬 int 行為的類別 X,重載運算子並輸出追蹤:
X a(10), b(6);
cout << "sum:" << a + b << " a:" << a++ << " b:" << b++ << endl;
GCC 4.5 執行輸出:
construct 10
construct 6
operator ++(post) 6 // b++ 先執行
copy construct 6 // 產生臨時物件
operator ++(post) 10 // a++ 再執行
copy construct 10 // 產生臨時物件
operator + // 最後才執行 a+b
construct 18 // 此時 a=11, b=7, 結果 18
sum:18 a:10 b:6
desconstruct 18
desconstruct 10
desconstruct 6
desconstruct 7 // a 變 11
desconstruct 11 // b 變 7
cout << A << B << C 解析為 operator<<(operator<<(operator<<(cout, A), B), C)。
C++ 標準(C++17 前)未規定函式參數求值順序,編譯器可自由選擇。GCC 採用從右至左壓棧:
b++ → 產生臨時值 6,b 變 7a++ → 產生臨時值 10,a 變 11a+b → 此時 a=11, b=7 → 結果 18輸出時依串流順序吐出:18 10 6。
a++ + a++),行為未定義。這道題讓我明白:C++ 的水深在基礎細節,而非花俏特性。筆試題往往正是考驗這些「基本功」。