开发者社区> 问答> 正文

K&R里面第七章的一个cat程序中遇到的fclose()问题

此程序是一个简单版本的cat程序,也就是主要把将几个文件的内容粘到一起输出到stdout中,若在执行程序的 时候没有指定任何文件名,则默认由stdin读取输入。源程序如下:

#include <stdio.h>
 
int main(int argc, char *argv[]) {
    FILE *fp;
    void filecopy(FILE *, FILE *);
     
    if (argc == 1) 
        filecopy(stdin, stdout);
    else
        while (--argc > 0) 
            if ((fp = fopen(*++argv, "r")) == NULL) {
                printf("cat: can't open file %s\n", *argv);
                return 1;
            } else {
                filecopy(fp, stdout);
                fclose(fp);
            }
    return 0;
}
 
 
/* copy the content from input file(stream) to the output file        */
void filecopy(FILE *ifp, FILE *ofp) {
    int c;
 
    while ((c = getc(ifp)) != EOF)
        putc(c, ofp);
}

我的试验文件是两个分别名为mes1和mes2的文件。mes1的内容为“hello”,而mes2的内容为“marry!”,并且两个文件的末端都是没有换行符的,也就是说他们都仅仅只是一行末尾不带换行符的字符串而已。当我在shell执行这个程序时,出现下面的输出:

[garvylo@utopia test]$ ./a.out mes1 mes2
hello
marry!
[garvylo@utopia test]$

也就是说,在mes1和mes2的信息被输出到stdout时都在其尾部被加上了一个换行符,这是为什么呢?

之后,我在源文件中,“ return 0;” 前面加上了" while(1); "语句,目的是想看看程序在没有退出main()之前有没有将stdout的buffer中的内容输出至stdout连接的终端(显示器)。本来,理论上,应该在加了这句“ while(1); ”之后,程序是不会把buffer中积累的数据输送到stdout终端的,因为据我所知,stdout是行缓存的(在本机试验过也确实如此),在其没有写满buffer或遇到换行符之前是不会写到stdout终端的,只有在正常结束main()函数,再由exit()函数调用fclose(),由其调用fflush()函数才会把缓存区的东西输送到stdout终端。所以,这里感觉挺疑惑的。是不是那个" fclose(fp); "的作用呢?

另外,那个“ fclose(fp); ”是不是只是解除外部文件与文件指针fp的连接,释放文件指针和此文件的缓存区呢?它对于stdout应该没有影响吧?因为,我觉得在filecopy()函数中,只是由input file 的buffer读取数据,然后存到一个中间变量c,再把c中的内容存放到stdout终端的buffer中,input file 的buffer 好像与stdout终端的buffer没有什么直接的联系。不知道是否如此?

还有最后一个问题,我在网上搜寻过了却也没有得到答案。就是那个fopen()函数, 当以“ r ”读模式打开某个文件(假设这个文件位于disk上,并且是全缓冲的)时,是不是在为这个文件开辟了缓存区后,立即由文件读取内容填满这个buffer(若文件长度小于buffer长度,则读取完文件的全部内容即可),然后对该文件的读操作其实就是对该buffer的操作。当buffer的内容读完时,再通过系统调用读取该文件的剩余内容填于buffer中,当文件指针被释放后(fclose(fp)),其缓存也会被释放。而对于”调用fflush()函数刷新缓存使buffer中的内容输送到相应的被带开为“写(r)“的文件“这一说,在以” r “打开的文件中是不存在的?一个文件可以同时被打开为”读“和”写“模式吗?如果可以,那么它们申请的应该是两个不同的缓存空间吗?

晚辈还没有学习过操作系统的知识,所以问题问得可能有点浅显,但网上搜寻不出答案,而自己也百思不解,故老烦各位前辈指教一二。

展开
收起
a123456678 2016-06-13 15:44:54 2722 0
1 条回答
写回答
取消 提交回答
  • 老实说,你@我之前,我就看过你这个问题,但是我觉得挺棘手的,因为我自己觉得我也搞不定,所以没敢开腔,但是,被你@了,不站出来似乎有点乌龟了……

    1 “在mes1和mes2的信息被输出到stdout时都在其尾部被加上了一个换行符”,这个确实是不应该的,我在Windows下用vs2010试验过,并没有这样。至于linux为什么这样,我也不知道。

    2 “stdout是行缓存的(在本机试验过也确实如此)”,那你是如何证明这一点的呢?h或许你真的证明了,那我只能说linux的行为对我来说有点费解。

    你觉得fclose()另外一个文件,会强迫stdout输出吗?可是为了实际证明这一点,你把fclose()注释掉,试一下。或许我说的是错的。

    3 “input file 的buffer 好像与stdout终端的buffer没有什么直接的联系”,肯定是这样的,要联系也是程序让他们有联系,否则不乱套了?

    4 文件以"r"模式打开,而且文件是全缓冲,我的理解是缓冲区中的内容不是自动就填充的,而是你使用fread()或其他读的函数,系统会将读取的内容自动放到这个缓冲区,并返回你的读取函数。如果下次读的数据还是这个缓冲去范围内的数据,就直接从这个缓冲去复制,不直接读文件了。但是如果不在范围,还是要实际读取文件的,从而再次更新缓冲区。

    5 根据我知道的资料,fflush()是把缓冲区中的数据刷新到磁盘(真正的写入)。对于以读模式打开的文件,应该不需要同步,因为根本没有也不能修改文件;

    6 文件可以被同时打开为读和写。很多时候都是一个线程写文件,而另一个线程从这个文件读取,这算是线程通讯的一种办法。只要文件指针(读写位置)保持正确就可以了。实际上,在windows下面文件要想这样,必须打开时共享读写,不能独占式访问。但是简单的c库函数做不到这一点,能不能,听天由命了(要看读写这个文件的其他进程是否设置共享)。

    7 “如果可以,那么它们申请的应该是两个不同的缓存空间吗?",我要说的是一个FILE *指针对应一个缓冲区,如果只用一个,那不乱套,你写乱我的,我搞乱你的。多从常识,原理角度考虑问题,常识,原理应该这样,那么程序也应该这样。否则,程序也会出现问题的。

    2019-07-17 19:36:13
    赞同 展开评论 打赏
问答分类:
问答地址:
问答排行榜
最热
最新

相关电子书

更多
低代码开发师(初级)实战教程 立即下载
冬季实战营第三期:MySQL数据库进阶实战 立即下载
阿里巴巴DevOps 最佳实践手册 立即下载