4、输入输出函数:printf格式化输出、scanf格式化输入、getchar与putchar、缓冲区问题

说到C语言的输入输出,很多人第一反应就是printf和scanf。嗯,这两个函数确实是我们打交道最多的。但说实话,真正把它们用明白的人,我见过的不多。

我在项目中遇到过好几次线上bug,最后定位到都是输入输出函数用错了。比如格式化字符串写错了,或者缓冲区没清干净。这些小问题,排查起来特别费劲。今天咱们就把这块彻底捋清楚。

4.1 printf格式化输出

printf说白了就是「按格式打印」。它的核心是格式化字符串 + 可变参数。

核心语法:

int printf(const char *format, ...);

返回值是成功打印的字符数,这个很多人会忽略。

常用的格式占位符,我列个表给你看:

占位符 含义 示例
%d 有符号十进制整数 printf("%d", 42);
%f 浮点数(默认6位小数) printf("%.2f", 3.14159);
%s 字符串 printf("%s", "hello");
%c 单个字符 printf("%c", 'A');
%p 指针地址 printf("%p", &a);
%x 十六进制(小写) printf("%x", 255); // ff

宽度和精度控制:

printf("%10d", 42);      // 右对齐,宽度10
printf("%-10d", 42);     // 左对齐,宽度10
printf("%.5f", 3.14);    // 保留5位小数
printf("%010d", 42);     // 补零,宽度10

我的习惯:调试时我经常用 %p 打印指针地址,配合 %d 看值,排查野指针问题特别管用。

4.2 scanf格式化输入

scanf是printf的「反向操作」——从标准输入读取格式化数据。但这里有个大坑:变量必须传地址。

int age;
scanf("%d", &age);   // 注意 & 符号

char name[50];
scanf("%s", name);   // 数组名本身就是地址,不需要 &

我曾经踩过的坑:有一次写学生管理系统,忘了在int变量前加&,结果程序直接崩溃。排查了半小时才发现是scanf的问题。记住:除了数组名和指针,其他变量都要加&。

scanf的返回值:

int ret = scanf("%d %d", &a, &b);
// ret 表示成功匹配并赋值的变量个数
// 如果返回0,说明一个都没匹配上

我建议你养成检查返回值的习惯。为什么?因为用户输入可能不合法,你不检查的话,变量里就是垃圾值。

4.3 getchar与putchar

这两个函数是字符级别的输入输出。简单,但很实用。

int ch = getchar();   // 读取一个字符
putchar(ch);          // 输出一个字符

注意getchar的返回值是int,不是char。为什么?因为要返回EOF(-1)表示文件结束或出错。如果用char,-1会被截断。

典型用法:清空输入缓冲区

int c;
while ((c = getchar()) != '\n' && c != EOF);
// 吃掉缓冲区里剩余的所有字符,直到换行

我在项目中经常用这个技巧。比如用户输入完数字后按回车,缓冲区里还留着换行符。下次用getchar读取时,直接读到换行就结束了。这时候就需要清空缓冲区。

4.4 缓冲区问题

这是本章的重头戏。缓冲区问题,说白了就是「数据在内存里攒着,没及时处理」。

C语言的输入输出默认是行缓冲模式。什么意思?就是数据先存到缓冲区里,遇到换行符才真正执行I/O操作。

常见的缓冲区问题:

  1. scanf后跟getchar:scanf读取数字后,换行符留在缓冲区。下一个getchar直接读到换行符,导致「跳过」了真正的输入。
  2. 多次scanf混合:比如先读整数再读字符串,中间可能有空格或换行符干扰。
  3. printf不立即输出:有时候printf的内容没有立刻显示在屏幕上,因为还在缓冲区里。

解决方案:

// 方案1:用getchar吃掉换行符
scanf("%d", &n);
getchar();  // 吃掉换行符

// 方案2:用空格跳过空白字符
scanf("%d ", &n);  // 注意%d后面的空格

// 方案3:强制刷新缓冲区
printf("hello");
fflush(stdout);  // 强制输出

注意:fflush(stdin) 是未定义行为!虽然有些编译器支持,但标准C不保证。别这么写。

为什么会这样?因为标准输入和标准输出是分开的缓冲区。输入缓冲区由键盘输入触发,输出缓冲区由换行符或fflush触发。

我建议你记住一个原则:输入输出类型要统一。要么全用scanf/printf,要么全用getchar/putchar。混着用最容易出问题。

知识体系图

C语言输入输出 printf格式化输出 scanf格式化输入 getchar/putchar 缓冲区问题 格式占位符 宽度/精度 地址传递 返回值检查 字符读写 EOF处理 行缓冲模式 清空缓冲区

这张图把本章的知识点串起来了。你看,四个核心函数围绕「输入输出」这个中心,每个函数又有自己的子知识点。缓冲区问题单独拎出来,因为它太容易出错了。

总结一下:

  • printf:注意格式匹配,多用宽度和精度控制
  • scanf:别忘了取地址,检查返回值
  • getchar/putchar:返回值用int,注意EOF
  • 缓冲区:统一输入输出方式,必要时清空缓冲区

嗯,这些内容看起来简单,但真正写代码时很容易忽略细节。我建议你每个函数都亲手写几个例子跑一跑。特别是缓冲区问题,光看理论是不够的,得自己踩一次坑才能记住。

可可精品资料库