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

C 語言輸入函數大比拼:scanf vs gets vs fgets,誰才是字串讀取的正解?

     編輯:關於C語言

初學 C 語言時,scanf("%s", str) 和 gets(str) 是最常見的字串讀取方式,但它們各自有嚴重缺陷:前者遇空白截斷,後者極其危險且已被標準移除。本文深度對比三者行為,並給出現代 C 開發的最佳實踐。

核心差異速查表

函數 頭文件 能否讀取空白 緩衝區溢出保護 殘留換行符處理 標準狀態
scanf("%s") stdio.h ❌ 遇空格/Tab/換行停止 ❌ 無長度限制(需用 %99s) 換行符留在 stdin C89~C18 標準
gets() stdio.h ✅ 讀整行(含空格) ❌ 完全無保護,必導致溢出 吞掉換行符,不存入 C11 移除,C99 棄用
fgets() stdio.h ✅ 讀整行(含空格) ✅ 指定最大讀取長度 保留換行符(若緩衝區夠大) 推薦標準函數

經典坑點演示

場景 1:scanf 留下的換行符干擾後續 gets

char ch1[10], ch2[10];
scanf("%s", ch1);  // 輸入 "asdfg\n",ch1="asdfg",\n 留在緩衝區
gets(ch2);         // 直接讀到殘留的 \n,ch2=""(空字串)!

修正:scanf("%s", ch1); getchar(); // 吞掉殘留 \n 或改用 fgets 統一讀行再解析。

場景 2:gets 的緩衝區溢出漏洞

char buf[8];
gets(buf); // 輸入超過 7 字元直接覆蓋棧,程式崩潰或被攻擊

這是歷史上無數緩衝區溢出攻擊的溫床,嚴禁在任何生產代碼中使用 gets。

現代最佳實踐:fgets + 後處理

#define MAX_LINE 1024
char line[MAX_LINE];

if (fgets(line, sizeof(line), stdin)) {
    // 1. 移除尾部換行符(若存在)
    size_t len = strlen(line);
    if (len > 0 && line[len - 1] == '\n') line[len - 1] = '\0';

    // 2. 解析:sscanf / strtok / 手寫解析器
    // sscanf(line, "%d %f %s", &a, &b, str);
}

優勢

  • 安全:fgets 保證不超過緩衝區。
  • 完整:保留整行內容,含空白、Tab。
  • 可控:讀行與解析分離,便於錯誤恢復與多格式兼容。

補充:scanf 讀字串的安全寫法

若必須用 scanf 讀單詞(不含空白),請永遠加寬度限制:

char buf[100];
scanf("%99s", buf); // 最多讀 99 字元,留 1 位給 \0

總結

  • 棄用 gets:歷史垃圾,C11 已移除,見到即重構。
  • 慎用 scanf 讀字串:易殘留換行符、難防溢出,僅適合簡單單詞讀取。
  • 首選 fgets:安全、標準、靈活,配合 sscanf 或手寫解析器,是工業級 C 代碼讀取輸入的標準模式。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved