今天为了搞明白buffer的机制弄了很久。。。以至于本来该做的事情都耽误了
在CSDN别人blog上面看到这么一段代码,很有意思
#include
#include
int main()
{
int c = 0;
int i = 0;
setbuf(stdout,malloc(5));
while((c = getchar()) != EOF)
{
putchar(c);
#if 1
printf("hello world!\n");
i++;
if(i>5)
{
return 0;
}
#endif
}
return 0;
}
测试结果:
jasonleaster@ubuntu:~/Desktop$ ./c.out
a
b
c
ahello world!
hello world!
bhello world!
hello world!
chello world!
hello world!
首先,那个预处理#if 1 #endif 在预处理阶段,1为真,于是if 和endif之间的内容被编译
代码就简化成下面这个样子了
#include
#include
int main()
{
int c = 0;
int i = 0;
setbuf(stdout,malloc(5));
while((c = getchar()) != EOF)
{
putchar(c);
printf("hello world!\n");
i++;
if(i>5)
{
return 0;
}
}
return 0;
}
void setbuf(FILE *restrict fp ,char *restrict buf );
setbuf是把文件流fp的buffer设置为buf
void *malloc(size_t size);
malloc是向堆申请一个size大小的连续内存,注意!是连续的!这非常关键
malloc返回值是一个空指针,指向申请的这块大小为size字节的区域
setbuf(stdout,malloc(5));
于是这里就把stdout的buffer设置为了malloc申请来的5个字节大小的内存区域。
while((c = getchar()) != EOF)
{
putchar(c);
printf("hello world!\n");
i++;
if(i>5)
{
return 0;
}
}
从标准输入读取字符,然后赋值给c,只要不是ctrl+D产生的EOF,一切OK。
然后putchar打印这个字符c
注意,这里getchar,putchar都是标准库函数,所以他们都是使用标准输入输出流的
这里putchar将变量c对应的ascii字符输出到标准输出流
而这个过程中是要经过buffer的
jasonleaster@ubuntu:~/Desktop$ ./c.out
a
b
c
我的输入是abc。
错!
我的输入应该是‘a’'\n'‘b’'\n'‘c’'\n'
每次输入abc都伴有回车换行字符的输入!
那么在buffer中的状态应该就是这样的
我们暂且不讨论那第六个格子的问题
可以很清楚的看出输入信息在buffer中的状态
这个时候是
while((c = getchar()) != EOF)
{
putchar(c);
printf("hello world!\n");
i++;
if(i>5)
{
return 0;
}
}
然后printf等待它前面的putchar写入stdout之后,printf再将hello world写入buffer
这样
jasonleaster@ubuntu:~/Desktop$ ./c.out
a
b
c
就会
将‘a’写入buffer然后将printf要打印的字符写入buffer,i++,
然后'\n'写入buffer,然后将printf要打印的字符写入buffer,i++,
将‘b’写入buffer然后将printf要打印的字符写入buffer,i++,
然后'\n'写入buffer,然后将printf要打印的字符写入buffer,i++,
将‘c’写入buffer然后将printf要打印的字符写入buffer,i++,
然后'\n'写入buffer,然后将printf要打印的字符写入buffer,i++,
此时i == 6
if(i>5)
{
return 0;
}
这个时候才有输出!此时刷新stdout的buffer。输出到stdout
(gdb) list
1 #include
2 #include
3
4 int main()
5 {
6 int c = 0;
7 int i = 0;
8 //char buffer[10];
9 setbuf(stdout,malloc(5));
10 while((c = getchar()) != EOF)
(gdb) b 10
Breakpoint 1 at 0x4006d1: file ./test.c, line 10.
(gdb) run
Starting program: /home/liuzjian/Desktop/d.out
Breakpoint 1, main () at ./test.c:10
10 while((c = getchar()) != EOF)
(gdb) step
a
12 putchar(c);
(gdb) step
14 printf("hello world!\n");
(gdb)
15 i++;
(gdb)
16 if(i>5)
(gdb)
10 while((c = getchar()) != EOF)
(gdb)
12 putchar(c);
(gdb)
14 printf("hello world!\n");
(gdb)
15 i++;
(gdb)
16 if(i>5)
(gdb)
10 while((c = getchar()) != EOF)
(gdb)
b
12 putchar(c);
(gdb)
14 printf("hello world!\n");
(gdb)
15 i++;
(gdb)
16 if(i>5)
(gdb)
10 while((c = getchar()) != EOF)
(gdb)
12 putchar(c);
(gdb)
14 printf("hello world!\n");
(gdb)
15 i++;
(gdb)
16 if(i>5)
(gdb)
10 while((c = getchar()) != EOF)
(gdb)
c
12 putchar(c);
(gdb)
14 printf("hello world!\n");
(gdb)
15 i++;
(gdb)
16 if(i>5)
(gdb)
10 while((c = getchar()) != EOF)
(gdb)
12 putchar(c);
(gdb)
14 printf("hello world!\n");
(gdb)
15 i++;
(gdb)
16 if(i>5)
(gdb)
18 return 0;
(gdb)
23 }
(gdb)
0x00007ffff7a33ea5 in __libc_start_main () from /lib/x86_64-linux-gnu/libc.so.6
(gdb)
Single stepping until exit from function __libc_start_main,
which has no line number information.
ahello world!
hello world!
bhello world!
hello world!
chello world!
hello world!
[Inferior 1 (process 9873) exited normally]
接下来讨论那个malloc(5)
这是个。。。很糟糕的做法
但是我还是讨论问题吧。。。不吐槽了
这里malloc了5个byte当buffer
其实这里越界了,但是由于malloc是在堆上面申请的内存,这样是连续的,导致程序看起来没问题
如果把那个malloc的5个byte换成数组就有问题了
#include
#include
int main()
{
int c = 0;
int i = 0;
char buffer[10];
setbuf(stdout,buffer);
while((c = getchar()) != EOF)
{
putchar(c);
#if 1
printf("hello world!\n");
i++;
if(i>5)
{
return 0;
}
#endif
}
return 0;
}
挂的妥妥的。。。
所以关于刷新缓冲区,在C里面就两种
第一显示的调用fflush(streams);
第二程序结束
那种printf里面加个'\n'是不会刷新缓冲区的
C++里面貌似endl的时候会刷新,没玩过,不知道。
对于这个有意思的代码就讲这么多啦。。
有错漏欢迎指正
jasonleaster
update: 2014.03.24晚
如果用fwrite到stdout的话,在自定义的buffer情况下,应该必须强制刷新,不然不会输出到stdout
测试过了。
这里就很尴尬了,缓冲区自动刷新具体条件就很模糊了。程序结束不一定刷新缓冲区。。。