經典面試題:
printf("%f", 5); // 輸出 0.000000
printf("%d", 5.01); // 輸出一個極大整數
為何會產生這種看似「錯誤」的結果?本文從呼叫慣例、堆疊佈局、參數傳遞順序、浮點數儲存格式四個維度徹底拆解。
| 呼叫 | 格式字串 | 實際引數 | printf 內部解讀 | 結果 |
|---|---|---|---|---|
| 1 | %f | int (4 位元組) | double (8 位元組) | 取 8 位元組 → 高 4 位元組為垃圾值 → 0.000000 |
| 2 | %d | double (8 位元組) | int (4 位元組) | 僅取低 4 位元組 → 非預期大數 |
printf("%f %d", 5, 5.01) 先壓 5.01(高位址),再壓 5(低位址)
高位址 ┌──────────────┐
│ 5.01 (double)│ ← 8 bytes: 符號位(1) + 指數(11) + 尾數(52)
├──────────────┤
│ 5 (int) │ ← 4 bytes: 0x00000005
低位址 └──────────────┘
當 %f 期望 8 位元組時,會連同 5 的 4 位元組 + 5.01 的低 4 位元組組成一個非法 double。
printf("%f %d", 5, 120.5)120.5 的 IEEE 754 雙精度表示:
符號位 0 | 指數 10000000101 | 尾數 111000100000...0 低 4 位元組全為 0 → %f 解讀為 0.000000 高 4 位元組非零 → %d 解讀為大數
實測輸出:0.000000 1079574528(高 4 位元組解讀結果),完全吻合理論。
printf("%f %d", 5, 5.01) 同列輸出%f 取 5 (4B) + 5.01 低 4B → 非零尾數 → 大浮點數%d 取 5.01 高 4B → 大整數%f 後接大量 0%f/%lf 對應 float/double,%d 對應 int-Wformat (GCC/Clang) 可偵測不匹配iostream、fmt 庫、或自訂封裝printf 的變參機制建立在「呼叫者知曉型別、被呼叫者盲目信任格式字串」的契約上。理解堆疊傳遞與資料表示,才能在除錯時從記憶體層面定位此類隱蔽錯誤。