首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

scanf 函式完全指南:格式字串、常見錯誤與最佳實踐

     編輯:關於C語言

scanf 是 C 標準庫的格式化輸入函式,從 stdin 讀取並解析資料。雖然常用,但其微妙行為 (如緩衝區殘留、格式匹配失敗) 是新手主要bug來源。本文系統整理用法與避坑指南。

基本語法

int scanf(const char *format, ...);
  • 回傳:成功賦值的項目數;EOF 表示輸入失敗/結束。
  • 參數:必須傳址 (&var),字串/陣列例外 (本身即地址)。

格式字串組成

類別說明範例
格式指定符 以 % 開頭,指定讀取型別 %d, %f, %s, %c, %lf, %p
空白字元 空格、換行、Tab:跳過任意多個空白 "%d %d" 可讀 "1 2" 或 "1\n2"
非空白字元 需精確匹配輸入中對應字元 "%d,%d" 需輸入 "1,2"

常用格式指定符速查

%d  有號十進位整數      %i  自動識別進位 (0x/0 開頭)
%u  無號十進位整數      %o  八進位
%x  十六進位            %f  float (單精度)
%lf double (雙精度)     %Lf long double
%c  單一字元 (不跳過空白)  %s  字串 (遇空白停止)
%p  指標位址            %n  已讀取字元數 (寫入指向的 int)
%[] 掃描集合 (如 %[^\n] 讀整行)

修飾符

  • 長度:hh (char), h (short), l (long/double), ll (long long), L (long double), z (size_t), t (ptrdiff_t), j (intmax_t)
  • 寬度:%10s 最多讀 10 字元 (防緩衝區溢位)
  • 抑制賦值:%*d 讀取但不存儲

五大經典陷阱與解法

1. 忘記取址符 &

int a; scanf("%d", a);   // 錯誤:傳值,導致未定義行為
scanf("%d", &a);         // 正確

2. %s 遇空白截斷,無法讀含空格字串

char str[100];
scanf("%s", str);  // 輸入 "hello world" 僅讀 "hello"
// 解法:用 fgets 或掃描集合
fgets(str, sizeof(str), stdin);           // 含換行
scanf("%99[^\n]", str); getchar();        // 讀至換行,清除殘留 \n

3. %c 讀取殘留換行符

int a; char c;
scanf("%d", &a);
scanf("%c", &c);  // 直接讀到上一行的 \n
// 解法:在 %c 前加空白跳過空白
scanf(" %c", &c);

4. 輸入不匹配導致無限迴圈

while (scanf("%d", &x) != 1) {
    // 輸入非數字時,scanf 失敗且不消耗輸入,陷入死循環
    // 必須清除緩衝區
    int c; while ((c = getchar()) != '\n' && c != EOF);
}

5. 緩衝區殘留換行影響後續 fgets/gets

scanf("%d", &x);
getchar(); // 吞掉殘留 \n
fgets(buf, sizeof(buf), stdin);

安全替代方案

  • fgets + sscanf:先讀整行,再解析,完全控制緩衝區。
  • strtoX 系列:strtol, strtod 提供完整錯誤檢測、支援進位制、無緩衝區問題。
  • C11 Annex K:scanf_s 等邊界檢查函式 (MSVC 主導支援)。

最佳實踐清單

  1. 永遠檢查 scanf 回傳值,確認預期項目數。
  2. 字串輸入指定寬度:%99s。
  3. 混合輸入時,善用 " %c" 跳過空白;讀整行用 fgets。
  4. 避免在循環中直接用 scanf 做驗證,改用 fgets+sscanf 模式。
  5. 編譯加上 -Wall -Wextra,編譯器會捕捉格式字串與參數不匹配。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved