首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 更多程式語言 >> 程式設計疑難解答

C 語言輸入緩衝區殘留換行符導致 gets() 讀取異常的修復

     編輯:程式設計疑難解答

問題代碼

int N, n, i;
char t[1000], s[1000];
scanf("%d", &N);
while (N--) {
    gets(t);           // 讀取字串
    scanf("%d", &n);   // 讀取整數
    for (i = 0; i < n; i++) s[i] = t[i];
    s[n] = '\0';
    puts(s);
}

現象:第一輪 `gets(t)` 讀到空行,後續邏輯錯亂。

根本原因

`scanf("%d", &N)` 讀取整數後,**換行符 `\n` 殘留在標準輸入緩衝區**中。緊接著的 `gets(t)` 遇到此 `\n` 立即返回空字串(不包含換行符),導致實際要讀的字串被下一輪 `scanf("%d", &n)` 讀走,造成連鎖錯誤。

修復方案:清空輸入緩衝區

方法一:`fflush(stdin)`(Windows/MSVC 專用,非標準)

while (N--) {
    fflush(stdin);   // 清空輸入緩衝區(含殘留 \n)
    gets(t);
    scanf("%d", &n);
    ...
}

⚠️ C 標準規定 `fflush` 僅適用於輸出流,`stdin` 行為未定義。MSVC 實現了清空輸入緩衝區,但 GCC/Clang 等不支援,**不可移植**。

方法二:讀取並丟棄殘留字元(標準、可移植)

while (N--) {
    int c;
    while ((c = getchar()) != '\n' && c != EOF); // 吞掉殘留 \n
    gets(t);  // 仍不推薦,見下
    scanf("%d", &n);
    ...
}

方法三:全面替換 `gets()` → `fgets()`(最佳實務)

while (N--) {
    if (fgets(t, sizeof(t), stdin) == NULL) break; // 讀取含 \n
    t[strcspn(t, "\n")] = '\0'; // 移除末尾換行符(若有)
    scanf("%d", &n);
    // 清空 scanf 留下的 \n
    int c;
    while ((c = getchar()) != '\n' && c != EOF);
    for (i = 0; i < n && i < (int)strlen(t); i++) s[i] = t[i];
    s[n] = '\0';
    puts(s);
}

優勢:`fgets` 限制讀取長度(防溢位)、標準 C、可移植、行緩衝行為可控。

完整修正版代碼

#include <stdio.h>
#include <string.h>

int main() {
    int N, n, i;
    char t[1000], s[1000];

    if (scanf("%d", &N) != 1) return 0;
    // 吞掉首行 scanf 留下的換行符
    int c;
    while ((c = getchar()) != '\n' && c != EOF);

    while (N--) {
        if (fgets(t, sizeof(t), stdin) == NULL) break;
        t[strcspn(t, "\n")] = '\0'; // 去除換行

        if (scanf("%d", &n) != 1) break;
        // 吞掉 scanf 留下的換行符
        while ((c = getchar()) != '\n' && c != EOF);

        for (i = 0; i < n && t[i]; i++) s[i] = t[i];
        s[i] = '\0';
        puts(s);
    }
    return 0;
}

總結

  • 永遠不要使用 `gets()`:C11 標準已移除,極易緩衝區溢位。
  • `scanf` 族函數**不消耗尾部空白字元**(換行、空格),後續行讀取函數(`fgets`/`getchar`)必須先清理殘留。
  • 統一使用 `fgets` + `sscanf`/`strtol` 解析,或採用「讀整行 → 記憶體解析」模式,徹底避開掃描函數的緩衝區陷阱。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved