it编程 > 游戏开发 > ar

C 语言字符输入/输出:getchar、putchar 与缓冲区机制详解(实例代码)

70人参与 2026-08-24 ar

1. 引言

在 c 语言中,字符是文本处理的最小单位。无论是读取用户敲下的一个字母、处理文件中的一个字节,还是向屏幕输出一行提示,都离不开字符输入/输出。掌握字符 i/o 是理解标准输入输出流、缓冲区和更复杂文本处理的基础。

本文将围绕 c 标准库中最常用的两个字符函数展开:

通过原理讲解、代码示例和常见陷阱分析,帮助你彻底掌握 c 语言中的字符输入/输出。

2. 字符 i/o 的基本概念

2.1 标准输入与标准输出

c 语言通过三类标准流与外部环境交互:

流名称文件指针说明
标准输入 stdinstdin通常关联键盘
标准输出 stdoutstdout通常关联屏幕
标准错误 stderrstderr通常关联屏幕,用于输出错误信息

getchar()putchar() 本质上分别是 fgetc(stdin)fputc(c, stdout) 的简化形式,定义于 <stdio.h> 头文件中。

2.2 字符与整型

一个容易忽略的细节getchar() 的返回值类型是 int,而不是 char

原因在于,除了 0~255 的合法字符值外,到达文件末尾或发生错误时 getchar() 需要返回特殊值 eofeof 通常被定义为 -1。如果把返回值直接赋给 char 类型变量,-1 可能无法与合法字符区分,导致程序无法正确判断输入结束。

因此推荐用法是:

int c;
while ((c = getchar()) != eof) {
    // 处理字符 c
}

3. 常用输入输出函数详解

3.1 getchar:从标准输入读取一个字符

函数原型:

#include <stdio.h>
int getchar(void);

getchar() 每次调用会从标准输入流中读取并返回一个字符。如果输入流中没有数据,则程序会阻塞等待,直到用户输入内容并按下回车键。

返回值:

3.2 putchar:向标准输出写入一个字符

函数原型:

#include <stdio.h>
int putchar(int c);

putchar() 把参数 c 对应的字符写入标准输出流。注意虽然参数类型是 int,但内部只使用其低 8 位作为字符输出。

返回值:

标准输出 stdout 同样受缓冲机制影响。putchar() 调用成功后,字符首先被写入 stdout 的缓冲区,并不保证立刻显示到屏幕上:

因此,下面的代码在部分环境中可能看不到提示语:

#include <stdio.h>
int main(void) {
    printf("请输入字符:");
    putchar('>');           // 没有换行,可能暂时看不到
    fflush(stdout);         // 立即刷新输出缓冲区
    return 0;
}

常见的刷新时机包括:写入换行符、缓冲区写满、调用 fflush(stdout)、程序正常结束,以及部分流在读取输入前自动刷新。理解这一点可以避免「提示语没显示」或「输出顺序看起来不对」的问题。

3.3 一个最简单的示例

以下程序从键盘读取字符,并在屏幕上原样输出,直到遇到文件结束符:

#include <stdio.h>
int main(void) {
    int c;
    printf("请输入字符(按 ctrl+d 或 ctrl+z 结束):\n");
    while ((c = getchar()) != eof) {
        putchar(c);
    }
    return 0;
}

在 linux/macos 终端中,通常按 ctrl+d 表示输入结束;在 windows 命令行中,通常按 ctrl+z 后回车。

3.4 printf:格式化输出

printf() 是 c 中最常用的格式化输出函数,定义在 <stdio.h> 中。它根据格式串把数据格式化后写入标准输出 stdout

函数原型:

#include <stdio.h>
int printf(const char *format, ...);

常用转换说明:

说明含义示例
%d / %i有符号十进制整数printf("%d", 42) 输出 42
%u无符号十进制整数printf("%u", 42u) 输出 42
%f浮点数printf("%f", 3.14) 输出 3.140000
%c单个字符printf("%c", 'a') 输出 a
%s字符串printf("%s", "hello") 输出 hello
%x / %x十六进制整数printf("%x", 255) 输出 ff
%%输出百分号本身printf("%%") 输出 %

示例:

#include <stdio.h>
int main(void) {
    int age = 20;
    double score = 92.5;
    printf("年龄:%d\n", age);
    printf("成绩:%.1f\n", score);   // 保留 1 位小数
    printf("百分号:%%\n");
    return 0;
}

示例中 %.1f 表示浮点数保留 1 位小数,是 printf() 格式控制能力的典型体现。

3.5 scanf:格式化输入

scanf() 用于从标准输入 stdin 按指定格式读取数据,并把结果写入对应变量的地址中。

函数原型:

#include <stdio.h>
int scanf(const char *format, ...);

示例:

#include <stdio.h>
int main(void) {
    int age;
    double score;
    printf("请输入年龄和成绩:");
    scanf("%d %lf", &age, &score);
    printf("年龄:%d,成绩:%.1f\n", age, score);
    return 0;
}

注意事项:

3.6 四种函数对比与使用场景

函数功能操作对象是否格式化典型场景
getchar()读取一个字符stdin逐字符处理、字符统计、过滤空白
putchar()输出一个字符stdout逐字符输出、大小写转换
scanf()按格式读取数据stdin读取整数、小数、单词等结构化输入
printf()按格式输出数据stdout输出变量、调试信息、格式化文本

选择建议:

4. 缓冲区机制:为什么输入总是不“即时”

很多初学者运行下面的程序时会感到疑惑:

#include <stdio.h>
int main(void) {
    int c;
    printf("输入一个字符:");
    c = getchar();
    printf("你输入了:%c\n", c);
    return 0;
}

运行后程序并不会在用户按下第一个键时立刻响应,而是要等到用户按下回车后才输出结果。原因是标准输入默认采用**行缓冲(line buffering)**策略。

4.1 三种缓冲模式

模式说明典型场景
全缓冲缓冲区满或显式刷新时才 i/o文件读写
行缓冲遇到换行符或缓冲区满时刷新交互式终端
无缓冲每次读写立即执行标准错误流 stderr

标准输入 stdin 在连接到终端时通常为行缓冲,因此用户的输入会被暂存在缓冲区中,直到按下回车,数据才真正交付给程序。

4.2 回显机制

在终端中,用户键入的字符会立即显示在屏幕上,这是终端自身的回显行为,并不代表程序已经读取了这些字符。等到按下回车后,getchar() 才逐个读取缓冲区内的字符。

4.3 用 while 循环消费完剩余输入

下面的程序提示用户输入一个字符,但如果用户输入了多个字符,多余的字符仍会残留,进而影响后续读取:

#include <stdio.h>
int main(void) {
    int c;
    printf("请输入一个字符:");
    c = getchar();
    // 消费掉本行剩余的所有字符,直到换行符
    while (getchar() != '\n')
        ;
    printf("你输入的字符是:%c\n", c);
    return 0;
}

上面的技巧常用于“只读取第一个字符,丢弃本行剩余内容”。

5. 字符计数、行计数与单词计数

字符 i/o 是很多文本统计程序的基础。下面实现一个统计输入中字符数、行数和单词数的程序:

#include <stdio.h>
#include <stdbool.h>
#include <ctype.h>
int main(void) {
    int c;
    long chars = 0;
    long lines = 0;
    long words = 0;
    bool in_word = false;
    while ((c = getchar()) != eof) {
        chars++;
        if (c == '\n') {
            lines++;
        }
        if (isspace(c)) {
            in_word = false;
        } else if (!in_word) {
            in_word = true;
            words++;
        }
    }
    printf("字符数:%ld\n", chars);
    printf("行数:%ld\n", lines);
    printf("单词数:%ld\n", words);
    return 0;
}

程序使用 <ctype.h> 中的 isspace() 判断空白字符,使用布尔变量 in_word 追踪当前是否处于单词内部,从而在单词起始处正确计数。

6. 大小写转换实战

字符 i/o 常用于逐字符变换。下面程序读取输入,将小写字母转换为大写字母,其他字符原样输出:

#include <stdio.h>
#include <ctype.h>
int main(void) {
    int c;
    while ((c = getchar()) != eof) {
        putchar(toupper(c));
    }
    return 0;
}

toupper() 同样来自 <ctype.h>,它会无条件返回大写形式,对非小写字母则原样返回。

7. 常见陷阱与错误

7.1 用 char 接收 getchar 返回值

错误示范:

char c;
while ((c = getchar()) != eof) {
    // ...
}

getchar() 返回 eof(即 -1)时,赋给 char 后可能变成 255 或溢出,导致循环条件永远无法与 eof 比较成功,可能陷入死循环。应使用 int 类型接收。

7.2 忘记处理换行符

下面这段代码看似能依次读取两个字符:

char c1 = getchar();
char c2 = getchar();

如果用户输入 a 后回车,第一个 getchar() 读到 'a',第二个 getchar() 立即读到了换行符 '\n',而不是用户期望的下一个字符。处理交互式输入时要格外注意缓冲区中残留的换行符。

7.3 混淆 eof 与实际按键

在终端中,eof 并不是键盘上某一个普通按键,而是由终端通过特殊按键组合(如 ctrl+dctrl+z)发送的信号。ctrl+z 在 windows 下往往需要按回车后才生效,ctrl+d 在 linux/macos 下通常需要在一行开头按下才能触发 eof。

8. 文件字符 i/o 的扩展

getchar()putchar() 只能作用于标准输入输出。若要对文件进行逐字符读写,可以使用功能更通用的 fgetc()fputc()

#include <stdio.h>
int fgetc(file *stream);
int fputc(int c, file *stream);

例如,将一个文本文件的内容复制到另一个文件:

#include <stdio.h>
int main(void) {
    file *in = fopen("input.txt", "r");
    file *out = fopen("output.txt", "w");
    if (in == null || out == null) {
        perror("打开文件失败");
        return 1;
    }
    int c;
    while ((c = fgetc(in)) != eof) {
        fputc(c, out);
    }
    fclose(in);
    fclose(out);
    return 0;
}

通过将操作对象从标准流扩展为任意文件指针,就可以用同样的思路处理文件内容。

9. 总结

字符输入/输出是 c 语言文本编程的基石:

到此这篇关于c 语言字符输入/输出:getchar、putchar 与缓冲区机制详解(实例代码)的文章就介绍到这了,更多相关c 语言getchar、putchar 与缓冲区机制内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!

(0)

您想发表意见!!点此发布评论

推荐阅读

Ubuntu系统如何部署Prometheus监控并使用cpolar实现公网访问?

08-23

快速解决Request entity too large错误问题

08-21

JVM垃圾收集器怎么选?Serial、Parallel、CMS对比分析

08-28

华为鸿蒙 HarmonyOS 7.0.0.102 SP8 版本推送:附更新内容汇总

08-18

Apache StreamPark 功能和使用场景介绍、使用步骤详细示例

08-18

Ubuntu更换USTC镜像源完整指南(AMD64 / AARCH64)

08-31

猜你喜欢

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论