這是一道經典的 C 語言未定義行為(UB)題目:當 printf 格式字符串與實際參數類型不匹配時,程序行為完全不可預測。
float n1 = 3.0;
double n2 = 3.0;
long n3 = 2000000000;
long n4 = 1234567890;
long n5 = 250;
long n6 = 213;
printf("%ld %ld %ld %ld %ld %ld\n", n1, n2, n3, n4, n5, n6);
%ld 對應 long int(有符號長整數)。float 參數傳遞時會默認參數提升為 double。前 6 個整數/指針參數通過寄存器傳遞:RDI, RSI, RDX, RCX, R8, R9;浮點參數通過 XMM0~XMM7。
調用 printf("%ld %ld %ld %ld %ld %ld", n1, n2, n3, n4, n5, n6) 時:
n1 (float→double) → XMM0n2 (double) → XMM1n3 (long) → RDIn4 (long) → RSIn5 (long) → RDXn6 (long) → RCXprintf 解析 %ld 時從整數寄存器/棧讀取,但前兩個參數在 XMM 寄存器中!結果:
%ld 讀取的是殘留在整數寄存器中的垃圾值(或之前調用遺留值)。%ld 正確讀取 n3~n6。n5、n6 輸出 250 和 213(正確),但前兩個是隨機數。float/double 佔 8 字節,long 佔 4 字節。棧佈局錯位導致後續參數讀取錯誤偏移,全亂。printf 並部分檢查,但標準不保證。
// 打印 float/double
printf("%f %f ", n1, n2); // %f 適用於 float(提升為double) 和 double
printf("%e %e ", n1, n2); // 科學計數法
printf("%g %g ", n1, n2); // 自動選擇 %f/%e
// 打印 long
printf("%ld %ld %ld %ld ", n3, n4, n5, n6);
// 混合打印
printf("float:%f double:%f long:%ld %ld %ld %ld\n", n1, n2, n3, n4, n5, n6);
-Wformat -Wformat=2(默認 -Wall 包含)會報警:warning: format ‘%ld’ expects argument of type ‘long int’, but argument 1 has type ‘double’/W4 開啟格式檢查。格式字符串與參數類型必須嚴格匹配。 這道題的「陷阱」在於誘導你關注具體輸出值,實則答案只有一個:**未定義行為,輸出不可信,代碼有缺陷**。養成開啟編譯器警告、使用靜態分析、必要時用 static_assert 或類型安全格式庫(fmtlib, iostream, std::format C++20)的習慣,從根源杜絕此類問題。