scanf 與 printf 是 C 語言最基礎也最易踩坑的 I/O 函數。本文系統梳理格式字串語法、返回值語義、緩衝區行為、常見錯誤與最佳實踐,助你寫出健壯的交互式程式。
%[flags][width][.precision][length]specifier
| 元素 | 說明 | 範例 |
|---|---|---|
| flags | - 左對齊 + 符號 空格 正數前加空格 # 替代形式 0 零填充 | %-10d %+d %05d |
| width | 最小寬度,* 表示由參數指定 | %10s %*d |
| .precision | 小數位數/最大字串長度,* 由參數指定 | %.2f %.*s |
| length | hh h l ll j z t L 對應 char/short/int/long/long long/intmax_t/size_t/ptrdiff_t/long double | %hhd %ld %lld %zu %Lf |
| specifier | d/i 整數 u 無符號 o/x/X 八/十六進制 f/F/e/E/g/G 浮點 c 字元 s 字串 p 指針 n 寫入已輸出字元數 %% 輸出 % | %d %x %f %s %p %% |
| 函數 | 原型 | 特點 |
|---|---|---|
| scanf | int scanf(const char*, ...) | 標準輸入,跳過空白字元(%c/%[ 除外) |
| fscanf | int fscanf(FILE*, const char*, ...) | 指定流,檔案/管道通用 |
| sscanf | int sscanf(const char*, const char*, ...) | 從字串解析,安全無副作用 |
| vscanf 等 | int vscanf(const char*, va_list) | 變參列表版,供封裝使用 |
EOF(通常 -1)表示讀取失敗或檔案結束且未賦值任何項目int a, b;
if (scanf("%d %d", &a, &b) != 2) {
// 處理輸入錯誤:清空緩衝區、提示重輸、或退出
while (getchar() != '\n'); // 簡易清行
}
scanf("%d", &n) 讀完整數後,\n 留在緩衝區。後續 scanf("%c", &c) 會直接讀到換行符。scanf(" %c", &c)(格式字串前加空格跳過空白),或改用 fgets+sscanf。scanf("%s", buf) 不限制長度,極危險。scanf("%19s", buf)(寬度 = 緩衝區大小 - 1),或用 fgets。%c 不跳過空白,%1s 會跳過並讀一個非空白字元。需求不同選對格式。%[^\n] 讀整行(含空格),%[0-9] 只讀數字。結尾需配合 %*c 吃掉分隔符。long x; scanf("%d", &x) 未定義行為。必須用 %ld。printf("%+010.4f\n", 3.14); // +00003.1400(符號、零填寬、4位小數)
printf("%-10s|\n", "left"); // "left |"(左對齊)
printf("%*d\n", 5, 42); // 寬度由參數 5 指定
printf("%.s\n", 3, "hello"); // 精度由參數 3 指定 -> "hel"
fgets(buf, sizeof(buf), stdin) + sscanf / strto* 系列(strtol, strtod 可檢測錯誤位置)snprintf 防止緩衝區溢位,C11 printf_s(可選)scanf 但嚴格限制寬度scanf 呼叫必須檢查返回值%19s)fgets+sscanf 逐行處理%.2f),避免科學記號驚喜%p 配合 (void*) 強轉掌握格式化 I/O 的精髓與陷阱,能讓你的 C 程式在交互、解析、日誌等場景既安全又高效。