| 函數 | 原型 | 源 | 返回值 | 關鍵特性 |
|---|---|---|---|---|
fgetc | int fgetc(FILE *stream) | 指定流 | 字符值(int) / EOF | 基礎單字符讀取 |
getc | int getc(FILE *stream) | 指定流 | 字符值(int) / EOF | 通常為宏實現,性能略優 |
getchar | int getchar(void) | stdin | 字符值(int) / EOF | 等價 getc(stdin) |
gets | char *gets(char *s) | stdin | s / NULL | 極度危險,C11 已移除 |
fgets | char *fgets(char *s, int n, FILE *stream) | 指定流 | s / NULL | 安全替代品,可控長度 |
所有字符輸入函數均為行緩衝:用戶輸入內容存入內核緩衝區,按 Enter 後程序才能讀取。關鍵行為:
\n 也會被讀取並返回int main() {
char ch; int cnt = 0;
puts("輸入字符 (EOF 結束):");
while (EOF != (ch = fgetc(stdin))) {
printf("讀到: '%c' (0x%02X)\n", ch, ch);
if (++cnt > 5) break;
}
printf("共讀 %d 個字符\n", cnt);
}
輸入 aEnterbEntercEnter 實際讀取序列:'a' '\n' 'b' '\n' 'c' '\n' —— 3 個可見字符 + 3 個換行符 = 6 次循環。
gets 之死與 fgets 之生gets:緩衝區溢出的元兇\n 或 EOF 停止,丟棄 \n,末尾加 \0warning: the 'gets' function is dangerousfgets:安全、可控、標準首選char buf[100];
if (fgets(buf, sizeof(buf), stdin)) {
// 成功讀取
// buf 包含內容 + 可能的 '\n' + 結尾 '\0'
}
行為細節:
n-1 個字符,留 1 位給 \0\n 會包含在緩衝區中(與 gets 關鍵差異)NULLn-1,剩餘字符留在流中,下次調用繼續讀取#define BUFSIZ 1024
int main() {
char buf[BUFSIZ];
char *p;
puts("=== gets (僅演示,勿生產使用) ===");
if (gets(buf)) {
puts("gets 讀取結果:");
puts(buf); // 自動換行
for (p=buf; *p; p++) putchar(*p); // 手動輸出,無額外換行
putchar('\n');
}
puts("=== fgets ===");
if (fgets(buf, BUFSIZ, stdin)) {
puts("fgets 讀取結果:");
puts(buf); // 自動換行,注意:buf 已含 \n,會雙換行
for (p=buf; *p; p++) putchar(*p); // 手動輸出,含原始 \n
}
}
輸入 a bEnter 兩次:
=== gets === a b a b === fgets === a b a b a b
fgets 保留 \n 導致 puts 雙換行,手動遍歷輸出可見原始 \n。
fgets 尾部換行符的標準模式char *line = fgets(buf, sizeof(buf), stdin);
if (line) {
size_t len = strlen(line);
if (len > 0 && line[len-1] == '\n')
line[len-1] = '\0'; // 移除換行符
// 處理 line...
}
fgetc/getcfgets + 去換行scanf 族(但錯誤恢復困難)sscanf/strtok/手動解析gets| 錯誤代碼 | 問題 | 修正 |
|---|---|---|
char c; c = getchar(); | 返回值截斷,無法區分 EOF | int c; c = getchar(); |
fgets(buf, 100, stdin); printf("%s", buf); | 雙換行 | 移除 \n 或用 fputs(buf, stdout) |
while (fgets(buf, n, fp)) { ... } | 最後一行無 \n 也正常處理 | 正確,但需注意行未滿緩衝區時無 \n |
gets(buf); | 緩衝區溢出漏洞 | 替換為 fgets(buf, sizeof(buf), stdin) |
掌握輸入函數的緩衝機制、換行符語義、長度邊界,是編寫健壯 C 程序的基礎功。選對工具,敬畏緩衝區,方能行穩致遠。