在解決 ACM 題目「統計字符串中數字個數」時,代碼邏輯看似正確,但運行結果錯誤。核心代碼片段如下:
int n;
char c[100];
scanf("%d", &n);
while(n--) {
gets(c); // 此處直接調用 gets
// ... 統計邏輯
}
輸入測試數據:
2
abc123
def456
預期輸出:3 和 3,實際首行輸出為 0。
scanf("%d", &n) 讀取整數 2 後,**緩衝區中殘留換行符 \n**。隨後 gets(c) 讀取該換行符並認為輸入結束,導致第一組字符串實際為空串,統計結果自然為 0。第二次循環才真正讀取到 abc123。
| 方案 | 代碼示例 | 優點 | 缺點/注意 |
|---|---|---|---|
| 方案一:消耗換行符 (推薦新手) | |
簡單直觀,無需修改後續邏輯 | 若輸入格式不規範 (如輸入 2 abc) 可能吞掉有效字符 |
| 方案二:scanf 格式字符串匹配空白符 | |
緊湊,標準庫行為 | \n 只匹配單個換行;%*c 吞掉任意單字符,若無殘留會阻塞等待輸入 |
| 方案三:全面棄用 gets,改用 fgets (最佳實踐) | |
安全、可控制長度、無緩衝區溢出風險,C11 標準已移除 gets | 需手動處理尾部換行符,稍顯繁瑣 |
針對競賽/工程場景,建議封裝通用讀行函數:
// 安全讀取一行,自動去除尾部 \n,返回實際長度
int read_line(char *buf, int maxlen) {
if (!fgets(buf, maxlen, stdin)) return -1; // EOF 或錯誤
int len = strlen(buf);
if (len > 0 && buf[len-1] == '\n') buf[--len] = '\0';
// 若行過長導致未讀完 \n,清空殘留緩衝區
if (len == maxlen - 1 && buf[maxlen-2] != '\n') {
int ch;
while ((ch = getchar()) != '\n' && ch != EOF);
}
return len;
}
// 主程式調用
int n; char line[1024];
scanf("%d", &n); getchar(); // 簡單場景足夠
while(n--) {
read_line(line, sizeof(line));
// 統計數字...
}
C 語言標準輸入流 stdin 為**全緩衝/行緩衝**,scanf 系列不消耗分隔符 (空白、換行),gets/fgets 以換行符為邊界。**混用兩類函數必須顯式處理殘留換行符**。現代 C 開發強制棄用 gets,統一使用 fgets + 手動去換行,是杜絕此類低級錯誤的根本之道。