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

C 語言輸入輸出陷阱:scanf 與 gets/getchar 混用導致的緩衝區殘留問題

     編輯:PHP 相關知識

問題現象

int m;
while (scanf("%d", &m)) {
    char str[10][10];
    for (int i = 0; i < 5; i++) {
        printf("請輸入第%d個字符:", i+1);
        scanf("%s", str[i]);
    }
}

執行結果:

3
請輸入第1個字符:請輸入第2個字符:1
請輸入第3個字符:2
請輸入第4個字符:34
請輸入第5個字符:3

第一次 printf 後直接跳過輸入,顯示第二個提示。

根因分析

  1. scanf("%d", &m) 讀取整數 3,留下換行符 \n 在 stdin 緩衝區。
  2. 進入迴圈,scanf("%s", str[0]):%s 會跳過空白字元(含 \n),直接等待非空白輸入,因此 printf 輸出後緊接著下一次 printf,使用者來不及輸入。
  3. 實際上 %s 讀取的是 1(給 str[0]),導致後續邏輯錯位。

解決方案

方案一:清理緩衝區(推薦)

int c;
while ((c = getchar()) != '\n' && c != EOF); // 讀取並丟棄剩餘字元

在 scanf("%d", &m) 後、進入迴圈前呼叫。

方案二:改用 fgets + sscanf

char line[100];
while (fgets(line, sizeof(line), stdin)) {
    if (sscanf(line, "%d", &m) != 1) continue;
    for (int i = 0; i < 5; i++) {
        printf("請輸入第%d個字符:", i+1);
        fgets(str[i], sizeof(str[i]), stdin);
        str[i][strcspn(str[i], "\n")] = '\0'; // 移除換行
    }
}

方案三:C++ cin(若允許 C++)

int m;
string s;
while (cin >> m) {
    for (int i = 0; i < 5; i++) {
        cout << "請輸入第" << i+1 << "個字符:";
        cin >> s; // 自動跳過空白,不留殘留
        strcpy(str[i], s.c_str());
    }
}

關鍵知識點總結

函數行為殘留字元
scanf("%d")讀整數,跳過前導空白留下後續空白(含 \n)
scanf("%s")讀字串,跳過前導空白留下後續空白
gets()讀整行(含空白),遇 \n 停止丟棄 \n,不留殘留
getchar()讀單一字元無
fgets()讀指定長度或遇 \n保留 \n 於緩衝區

最佳實踐建議

  • 避免混用:同一程式盡量統一使用 fgets 讀行,再用 sscanf 解析。
  • 檢查返回值:scanf 返回成功匹配項數,EOF 表輸入結束。
  • 棄用 gets:無緩衝區長度檢查,極易溢位,C11 已移除。
  • 跨平台換行:Windows \r\n、Linux \n,用 strcspn(str, "\r\n") 通用移除。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved