字符输入输出:getchar、putchar、getc、putc 的使用与区别
说到 C 语言的文件操作,字符级的输入输出是最基础、也是最容易被忽视的一块。我刚开始学 C 的时候,总觉得 printf 和 scanf 就够用了,干嘛还要学这些单个字符的函数?后来在项目中踩了坑才明白——有些场景,还真就得用它们。
说白了,getchar、putchar、getc、putc 这四位,就是 C 语言里处理单个字符的「四大金刚」。它们长得像,用法也像,但细节上各有各的脾气。
先看 getchar 和 putchar
getchar 从标准输入(通常是键盘)读一个字符。putchar 把一个字符写到标准输出(通常是屏幕)。用法极其简单:
#include <stdio.h>
int main() {
char ch;
printf("请输入一个字符:");
ch = getchar(); // 从键盘读一个字符
printf("你输入的是:");
putchar(ch); // 输出到屏幕
putchar('\n');
return 0;
}
注意看,getchar 返回的是 int 而不是 char。为什么?因为除了正常字符,它还可能返回 EOF(文件结束标志),而 EOF 通常定义为 -1,char 装不下。嗯,这里要注意——如果你用 char 去接返回值,在某些编译器上会出问题。
int 类型接收 getchar 的返回值,不要用 char。否则 EOF 可能被截断,导致死循环。
再看 getc 和 putc
getc 和 putc 跟上面两位功能类似,但多了一个参数——文件指针。它们可以从任意文件流中读写字符,不限于标准输入输出。
#include <stdio.h>
int main() {
FILE *fp = fopen("test.txt", "r");
if (fp == NULL) {
printf("文件打开失败\n");
return 1;
}
int ch;
while ((ch = getc(fp)) != EOF) {
putc(ch, stdout); // 输出到屏幕
}
fclose(fp);
return 0;
}
这段代码把文件内容一个字符一个字符地读出来,再打印到屏幕上。效率不高,但胜在简单直观。我在项目中曾经用它来逐字符解析配置文件,虽然慢了点,但逻辑特别清晰,调试起来也方便。
它们到底有什么区别?
其实 getchar 就是 getc(stdin) 的简写,putchar(c) 就是 putc(c, stdout) 的简写。标准库就是这么定义的:
#define getchar() getc(stdin)
#define putchar(c) putc(c, stdout)
所以从功能上讲,它们是一家人。但有一个细微差别:getc 和 putc 可能是宏实现,也可能是函数实现,具体看编译器。而 getchar 和 putchar 通常也是宏。这意味着什么?
- 宏实现的版本:速度快,但参数可能被多次求值。比如
putc(getc(fp), stdout)这种写法,如果putc是宏,getc(fp)可能被调用两次——一次取字符,一次取参数。嗯,这是个坑。 - 函数实现的版本:安全,但多一次函数调用开销。
ch = getc(fp),再 putc(ch, stdout)。别图省事写在一行里。
用一张图理清关系
下面这张 SVG 图展示了这四个函数的关系和典型用法:
实际项目中的选择
我个人习惯这样选:
- 只跟键盘和屏幕打交道:用
getchar和putchar,代码更短,意图更明确。 - 读写文件:用
getc和putc,因为需要传文件指针。 - 追求极致性能:用
getc_unlocked和putc_unlocked(线程不安全版本),但一般用不到。
我曾经在做一个嵌入式日志系统时,需要逐字符把日志写到串口。当时用了 putc 配合自定义的 FILE 流,效果很好。但有一次因为没注意 putc 是宏实现,在循环里写了 putc(getc(fp), stdout),结果字符被吞了一半……排查了半天才发现是参数多次求值的问题。从那以后,我写这类代码都格外小心。
一个完整的例子
下面这个程序演示了从键盘读取字符,直到遇到换行符为止,然后原样输出:
#include <stdio.h>
int main() {
int ch;
printf("请输入一行文字(回车结束):\n");
// 逐字符读取,直到遇到换行或 EOF
while ((ch = getchar()) != '\n' && ch != EOF) {
putchar(ch); // 边读边输出
}
putchar('\n');
return 0;
}
你想想看,如果这里用 scanf("%s"),遇到空格就停了。而用 getchar 逐字符处理,空格、制表符都能原样保留。这就是字符级函数的优势——精细控制。
- 四个函数都处理单个字符,返回
int类型 getchar/putchar专用于标准输入输出getc/putc可用于任意文件流- 注意宏实现的参数多次求值问题
- 始终检查返回值是否为
EOF
好了,这一节的内容就这些。字符输入输出虽然基础,但用好了能解决很多实际问题。下一节我们会聊到字符串的输入输出函数,到时候再对比一下它们的适用场景。