C语言中搭配scanf使用时puts行为异常而printf正常的原因是什么
问题原因解析
1. 核心诱因是scanf的使用错误,和puts无关
你当前的逻辑错误根源在scanf的格式符特性与返回值判断逻辑:
%c格式符不会自动跳过输入缓冲区中的空白字符(换行、空格、制表符都属于空白字符),而你每次输入完成后按下的回车会生成\n残留在缓冲区中- 你判断循环继续的条件是
scanf(...) != EOF,但scanf的返回值是成功匹配并赋值的参数个数,只有当输入流完全结束无任何可读内容时才会返回EOF,如果出现部分匹配(比如只匹配到了第一个%c,第二个%d匹配失败),返回值是成功匹配的参数个数(此时为1),仍然不满足退出条件,循环会继续执行。
以你输入的4行内容为例,执行流程如下:
- 每输入一行
c 数字加回车,缓冲区内容为c 数字\n - 第一次
scanf匹配到c和数字,返回2,打印一次foo,缓冲区剩余\n - 第二次循环执行
scanf:%c读走剩余的\n赋值给input,接下来匹配%d时遇到下一行开头的c匹配失败,scanf返回1,仍然不等于EOF,再次打印foo,c被留在缓冲区等待下一次读取 - 以上流程每一行输入对应2次循环执行,4行输入正好对应8次
foo输出,和你观测的结果完全一致。
2. 为什么换printf看起来就正常?
这是IO缓冲机制导致的错觉:
- 终端环境下
stdout默认是行缓冲,只有遇到换行符、缓冲区满、程序退出三种情况才会把缓冲区的内容刷到终端显示 puts("foo")等价于printf("foo\n"),自带换行符,每次调用都会触发缓冲刷新,所以你能实时看到每一次循环的打印结果- 如果你用的是
printf("foo")不加换行,所有打印内容都会堆积在缓冲区中,直到程序退出时才一次性输出,你就会误以为只打印了一次,实际上循环执行的次数完全没有变化。
3. 不属于未定义行为
该行为完全符合C语言标准的定义,是可预期的逻辑错误,不属于未定义行为。
修复方案
两种方案二选一即可:
- 给
%c前面加空格,让scanf自动跳过所有前置空白字符:while(scanf(" %c %d", &input, &data) == 2) { puts("foo"); } - 把循环判断条件改为判断成功匹配的参数个数为2,避免部分匹配时继续执行:
while(scanf("%c %d", &input, &data) == 2) { puts("foo"); }
内容的提问来源于stack exchange,提问作者NewRProgrammer
相关产品推荐
相关产品推荐

