格式控制字符串完整語法
% [標志] [寬度] [.精度] [長度修飾符] 類型字符
各組成部分詳解
1. 標志字符
| 標志 | 含義 |
| - | 左對齊(默認右對齊) |
| + | 有符號數總顯示正負號 |
| 空格 | 正數前加空格,負數顯示 - |
| 0 | 數值右對齊時左側填 0(而非空格) |
| # | 替代形式:八進制加 0,十六進制加 0x,浮點數強制顯示小數點 |
2. 寬度與精度
m:最小字段寬度,不足左填(或右填 -),超寬不截斷
.n:精度。整數→最小位數(左補 0);浮點→小數位數;字符串→最大字符數
*:由參數動態指定寬度/精度,如 printf("%*.*s", w, p, str)
3. 長度修飾符
| 修飾符 | 適用類型 | 含義 |
| hh | d,i,o,u,x,X | signed/unsigned char |
| h | d,i,o,u,x,X | short/int |
| l | d,i,o,u,x,X | long |
| l | f,F,e,E,g,G | double(printf 中 %lf 同 %f) |
| ll | d,i,o,u,x,X | long long |
| L | f,F,e,E,g,G | long double |
| z | d,i,o,u,x,X | size_t |
| t | d,i,o,u,x,X | ptrdiff_t |
| j | d,i,o,u,x,X | intmax_t |
4. 類型字符速查表
| 字符 | 輸出內容 | 示例 |
| d,i | 有符號十進制整數 | 42, -7 |
| u | 無符號十進制 | 42 |
| o | 八進制 | 52 |
| x,X | 十六進制 | 2a, 2A |
| c | 字符 | A |
| s | 字符串 | hello |
| f,F | 定點小數 | 3.141593 |
| e,E | 科學計數法 | 3.141593e+00 |
| g,G | 自動選 f/e 短者,去尾部 0 | 3.14159 |
| p | 指針地址 | 0x7ffe... |
| n | 已輸出字符數寫入 int* | (隱蔽) |
| % | 字面 % | % |
字符串格式化進階
%-10.5s // 左對齊,寬 10,最多取 5 字符
%10.5s // 右對齊,寬 10,最多取 5 字符
%*.*s // 寬度、精度由參數傳入
實戰範例
printf("%f%%\n", 1.0/3); // 0.333333%
printf("%d, %o\n", -1, -1); // -1, 177777(補碼八進制)
printf("%x\n", 255); // ff
printf("%#x\n", 255); // 0xff
printf("%05d\n", 7); // 00007
printf("%-10s|\n", "hi"); // "hi |"
安全與避坑
- %n 漏洞:格式化字符串攻擊利用點,現代編譯器默認禁用或需
%n$ 顯式啟用
- 參數不匹配:導致未定義行為,啟用編譯器警告
-Wformat
- 緩衝區溢出:
sprintf 改用 snprintf
- 浮點精度:float 僅 6~7 位有效數字,double 15~16 位,勿過度依賴默認 6 位小數
動態寬度精度技巧
int w = 10, p = 3;
printf("%*.*f\n", w, p, 3.14159); // 寬 10,精度 3 → " 3.142"
總結
掌握 printf 格式字符串的完整語法,能精確控制輸出樣式,避免常見 Bug。現代 C++ 推薦使用 iostream 或 fmt 庫(類型安全、可擴展),但 printf 仍是嵌入式、系統編程、跨語言調用的標準選擇。