something with buffer 有意思的缓冲区:关于setbuf()一段很有意思的代码

来源:互联网 发布:修复照片软件免费版 编辑:程序博客网 时间:2024/05/17 02:59

今天为了搞明白buffer的机制弄了很久。。。以至于本来该做的事情都耽误了

在CSDN别人blog上面看到这么一段代码,很有意思

#include<stdio.h>#include<stdlib.h>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<stdio.h>#include<stdlib.h>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

我的输入是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;                }        }


每getchar读入一个非EOF量,这个时候就被putchar到buffer里面

然后printf等待它前面的putchar写入stdout之后,printf再将hello world写入buffer

这样


jasonleaster@ubuntu:~/Desktop$ ./c.out
a
b

就会

将‘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;                }


于是return 0.程序结束

这个时候才有输出!此时刷新stdout的buffer。输出到stdout


(gdb) list1#include<stdio.h>2#include<stdlib.h>34int main()5{6int c = 0;7int i = 0;8//char buffer[10];9setbuf(stdout,malloc(5));10while((c = getchar()) != EOF)(gdb) b 10Breakpoint 1 at 0x4006d1: file ./test.c, line 10.(gdb) runStarting program: /home/liuzjian/Desktop/d.out Breakpoint 1, main () at ./test.c:1010while((c = getchar()) != EOF)(gdb) stepa12putchar(c);(gdb) step14printf("hello world!\n");(gdb) 15i++;(gdb) 16if(i>5)(gdb) 10while((c = getchar()) != EOF)(gdb) 12putchar(c);(gdb) 14printf("hello world!\n");(gdb) 15i++;(gdb) 16if(i>5)(gdb) 10while((c = getchar()) != EOF)(gdb) b12putchar(c);(gdb) 14printf("hello world!\n");(gdb) 15i++;(gdb) 16if(i>5)(gdb) 10while((c = getchar()) != EOF)(gdb) 12putchar(c);(gdb) 14printf("hello world!\n");(gdb) 15i++;(gdb) 16if(i>5)(gdb) 10while((c = getchar()) != EOF)(gdb) c12putchar(c);(gdb) 14printf("hello world!\n");(gdb) 15i++;(gdb) 16if(i>5)(gdb) 10while((c = getchar()) != EOF)(gdb) 12putchar(c);(gdb) 14printf("hello world!\n");(gdb) 15i++;(gdb) 16if(i>5)(gdb) 18return 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]



可以很明显的看出,stdout并不是程序执行过程中输出的,而是return 0;程序结束之后!

接下来讨论那个malloc(5)

这是个。。。很糟糕的做法

但是我还是讨论问题吧。。。不吐槽了

这里malloc了5个byte当buffer

其实这里越界了,但是由于malloc是在堆上面申请的内存,这样是连续的,导致程序看起来没问题

如果把那个malloc的5个byte换成数组就有问题了

#include<stdio.h>#include<stdlib.h>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;}




jasonleaster@ubuntu:~/Desktop$ ./d.out
a
b
c
*** stack smashing detected ***: ./d.out terminated
ahello world!


hello world!
bhello world!


hello world!
chello world!


hello world!
Aborted (core dumped)

挂的妥妥的。。。


所以关于刷新缓冲区,在C里面就两种

第一显示的调用fflush(streams);

第二程序结束


那种printf里面加个'\n'是不会刷新缓冲区的


C++里面貌似endl的时候会刷新,没玩过,不知道。

对于这个有意思的代码就讲这么多啦。。


有错漏欢迎指正

jasonleaster

 

update: 2014.03.24晚

如果用fwrite到stdout的话,在自定义的buffer情况下,应该必须强制刷新,不然不会输出到stdout

测试过了。

这里就很尴尬了,缓冲区自动刷新具体条件就很模糊了。程序结束不一定刷新缓冲区。。。





0 0
原创粉丝点击