stdout输出异常排查:仅单条输出,移除stderr输出后恢复正常
问题原因分析
这个问题的核心是C标准库的stdio缓冲机制加上终端输出的性能开销共同导致的,我来一步步拆解:
1. 先明确stdio流的缓冲特性
C标准库给不同的标准流默认设置了不同的缓冲模式:
stdout:当连接到终端时默认是行缓冲——只有遇到换行符\n、缓冲区被填满,或者显式调用fflush(stdout)时,才会把缓冲里的内容输出到终端。stderr:默认是无缓冲——所有写入stderr的内容会立刻输出到终端,不会被缓冲。
2. 为什么有stderr输出时stdout只显式一行?
你的代码里有两个关键行为:
- 每
100000000次循环才往stdout输出一行带\n的内容,理论上行缓冲会因为\n触发刷新,内容应该立刻显示。 - 但每次循环都往
stderr输出\rrunning %ld——这个操作的问题在于:- 首先,
stderr是无缓冲的,每次写入都要直接和终端交互,而终端的输出操作本身是比较耗时的,这会让你的循环执行速度变得极慢,要等很久才能到达下一个i%100000000==0的输出点,所以你误以为stdout只输出了一行,实际上程序还在慢悠悠地执行。 - 其次,
\r是回车符,它会把光标移到当前行的开头,覆盖之前的内容,这会让终端一直显示最新的状态,但也会让你忽略掉stdout后续输出(其实还没到输出时机)。
- 首先,
3. 为什么移除stderr输出后stdout恢复正常?
当你删掉fprintf(stderr, ...)这行代码后,循环不再有频繁的终端输出开销,执行速度会飙升,很快就能到达100000000、200000000等输出点,stdout的行缓冲会因为\n不断触发刷新,所以你能看到连续的输出。
修复建议
如果想要保留状态输出同时让stdout正常输出,可以优化stderr的输出频率,减少终端交互的开销:
#include <stdio.h> #include <limits.h> void work () { for (long i = 0; i < LONG_MAX; i++) { if (i % 100000000 == 0) { fprintf(stdout, "%ld\n", i); // 显式刷新stdout,确保内容立刻输出 fflush(stdout); } // 每10000次循环才更新一次状态,降低终端输出开销 if (i % 10000 == 0) { fprintf(stderr, "\rrunning %ld", i); } } return 0; } int main() { work(); fprintf(stderr, "\ndone\n"); return 0; }
内容的提问来源于stack exchange,提问作者cemerick
相关产品推荐
相关产品推荐

