函數原型與參數結構
int printf(const char *format, ...);
// 回傳:輸出字元數,錯誤回負值
// 參數:控制字串 + 可變參數列表
控制字串組成
- 普通字元:原樣輸出
- 轉義序列:
\n 換行、\t 定位、\\ 反斜線等
- 轉換說明符:
% 開頭,指定參數輸出格式
轉換說明符完整對照表
| 說明符 | 輸出型別 | 範例輸出 | 備註 |
%d, %i | 有號十進位整數 | 42, -7 | 同義 |
%u | 無號十進位整數 | 4294967295 | -1 輸出為 MAX_UINT |
%o | 八進位無號整數 | 1750 | |
%x, %X | 十六進位無號整數 | 7b, 7B | 小寫/大寫字母 |
%f | 十進位浮點數 | 3.141593 | 預設小數 6 位 |
%e, %E | 科學記號浮點數 | 3.141593e+00 | 指數小寫/大寫 |
%g, %G | 自動選擇 %f/%e | 3.14 / 1.23e+06 | 有效數字 6 位,去尾零 |
%a, %A | 十六進位浮點數 (C99) | 0x1.921fb5p+1 | 精確二進位表示 |
%c | 單一字元 | A | int 引數轉 unsigned char |
%s | 字串 | hello | char* 以 \0 結尾 |
%p | 指標位址 | 0x7ffeefbff5a0 | void* 參數,實作定義格式 |
%% | 字面 % 字元 | % | 輸出百分號本身 |
修飾符完整指南
位置:%[旗標][寬度][.精度][長度]說明符
旗標
| 旗標 | 效果 | 範例 |
- | 左對齊 (預設右對齊) | %-10d → "42 " |
+ | 強制顯示正負號 | %+d → "+42" |
空格 | 正數前加空格,負數顯示 - | % d → " 42" |
0 | 前導零填充 (數值型) | %05d → "00042" |
# | 替代形式 | %#x→"0x2a", %#o→"052", %#f→強制小數點 |
寬度與精度
| 語法 | 意義 | 適用說明符 | 範例 |
%10d | 最小欄寬 10,不足左補空格 | 所有 | " 42" |
%.3f | 小數點後 3 位 | f, e, E | 3.142 |
%.5s | 最多輸出 5 字元 | s | "hello" → "hello" |
%.3d | 最少 3 位數字,前導零 | d, i, o, u, x, X | 7 → "007" |
%10.3f | 欄寬 10,小數 3 位 | f, e, E | " 3.142" |
%.*f | 精度由引數指定 | f, e, E | printf("%.*f", 3, 3.14159) |
長度修飾符
| 修飾符 | 對應型別 | 範例 |
hh | signed char, unsigned char | %hhd, %hhu |
h | short, unsigned short | %hd, %hu |
| (無) | int, unsigned int | %d, %u |
l | long, unsigned long | %ld, %lu |
ll | long long, unsigned long long | %lld, %llu |
L | long double | %Lf, %Le |
z | size_t (sizeof 回傳) | %zu, %zd |
t | ptrdiff_t (指標差值) | %td, %tu |
j | intmax_t, uintmax_t | %jd, %ju |
實戰技巧與陷阱
1. 參數對應嚴格性
printf("%d %s", 42); // 錯誤:少參數 → 未定義行為
printf("%d", "hello"); // 錯誤:型別不符 → 型別懲罰
printf("%s", 42); // 嚴重:解讀 42 為位址 → Crash
2. 寬度/精度由引數傳遞
printf("%*d", 10, 42); // 寬度 10 → " 42"
printf("%.*s", 3, "hello"); // 精度 3 → "hel"
3. 字串輸出安全
char *p = NULL;
printf("%s", p); // Crash!
printf("%s", p ? p : "(null)"); // 安全寫法
4. 結構體輸出除錯巨集
#define PRINT_POINT(p) printf("Point(%d, %d)", (p).x, (p).y)
#define PRINT_RECT(r) printf("Rect[%d,%d %dx%d]", (r).x, (r).y, (r).w, (r).h)
5. 回傳值利用
int len = printf("Hello"); // len = 5
printf(" (%d chars)\n", len);
安全替代方案
snprintf(buf, size, fmt, ...):緩衝區長度限制,防溢位
vsnprintf:配合 va_list 實作自訂日誌函數
asprintf (GNU 擴充):自動配置記憶體
結語
printf 以極簡介面實現極致格式化輸出能力。精通轉換說明符、修飾符組合、長度修飾符對應型別三大核心知識體系,並養成參數嚴格對應、使用 snprintf 防溢位、善用回傳值的工程習慣,即能在除錯、日誌、報表、序列化等場景遊刃有餘。記住:格式字串即程式,編譯器無法完全驗證,測試與 Code Review 是最後防線。