当前位置:C++技术网 > 资讯 > C语言文件操作详细分析:1 FILE结构体和文件操作机制

C语言文件操作详细分析:1 FILE结构体和文件操作机制

更新时间:2015-06-23 19:46:27浏览次数:1+次

    平时在用或者学习C语言操作文件的时候,上来就是FILE* pFile; 这样的声明,然后就是调用各种函数来操作文件。老师们也都直接这样教,我们也基本就这样用而已。不过,突然发现,FILE到底是什么,一时就懵了。平时压根就没想这个问题。
    因为需要操作文件,发现自己对文件还是不清楚。想起了C语言的FILE,但是操作起来根本就是稀里糊涂的。始终都不知道文件操作内部到底是怎么操作的,以至于始终都不能清楚的认识文件操作,做不到深入理解和灵活运用。然而,在网上搜了大量的资料,也没有说明。看到好些国外的网站里的解释是说一般不用了解内部结构,知道怎么用就行了。也发现国外和国内很多人也对FILE一知半解,总是混淆一些东西。FILE到底是类型还是什么,很不清楚。最后查到一句话,是写定义FILE的作者写的,原文为:

I still believe that nobody in their right mind should make use of the internals of this structure. Provided by Pedro A. Aranda Gutiirrez。

大意就是说,他相信正常情况下是没有人需要利用这个结构体的内部结构成员的。因为他提供的函数都可以满足平常的操作,完全可以忽略内部结构,只要知道FILE*定义一个指针和其他函数配合使用即可。所以大家都不去了解这些内部结构了。老师也这样教,大家也这样用。甚至连问个为什么的人都没有了,或者少的可怜。在网上搜索大量的资料,只找到一个简要的说明,其他的都是说不用了解内部的,忽略即可。
    但是,要深刻的掌握,还是要了解内部的运作,这样你就可以理解的很深。而网上和大部分的书都对这个介绍的很少,基本只是大概的介绍运作流程,介绍文件操作函数就算可以了。然而,在网上还几乎搜不到相关的资料。所以本人通过程序验证测试,写一个分析提供大家参考。如果有不准确或者不正确的地方,请指出。如果有没解释清楚的地方也请指出,便于我更进一步的分析。

struct _iobuf
{
    char *_ptr;
    int  _cnt;
    char *_base;
    int  _flag;
    int  _file;
    int  _charbuf;
    int  _bufsiz;
    char *_tmpfname;
};
typedef struct _iobuf FILE;

    以上是VC在stdio.h文件中的定义。typedef struct _iobuf FILE;则重定义了这个结构体,就是我们常见的FILE结构体。而FILE* pFile;中的pFile则是FILE的结构体指针。结构体内部则会记录每次的文件操作的各种信息。结构体内部成员的介绍稍后详细介绍。
    结构体_iobuf从名字上可以看出这是一个IO缓冲结构体,然而从内部成员来看,则没有数组,而只有指针。说明结构体内部是不分配内存空间的,而只是记录文件信息,这也是这个结构体存在的目的。既然作为IO缓冲结构体,那缓冲在哪里定义的呢?你是否想过这个问题。而根据作者所说,使用者不必关注内部细节,而只需要会使用相关的函数即可。可想而知,文件缓冲必然是在相关的函数创建的,具体在哪些函数,在后续的文章进行详细分析。而我们平常使用FILE* pFile;也就表示我们只需要创建一个指针就可以了,其他的全部由文件函数管理。这也是为什么不建议大家直接操作FILE结构体的原因。经过测试,改变_base成员的值后,文件close时会崩溃,这也表明,这一套都是融合在一起的,自己不要随意修改FILE结构体的东西。但是我们需要了解这个结构体。那么,我们可以才想到,事实上,文件读写是通过了一个缓冲区的,这样可以提高读写效率。那么缓冲区的创建又不在文件函数外部,那就是在函数内部创建好,在fclose函数释放。这点可以肯定。fopen函数返回的是文件指针,其他函数传入的参数有函数指针。内存管理都是在函数内部执行的,我们不用操心。但是一定要记得打开文件后要关闭文件,避免程序运行时造成内存泄露。fopen返回的函数指针就传给了我们,通过文件指针,操作文件。文件操作函数在内部会自动填充和修改FILE结构体。

    结构体成员的详细解释:
    char *_ptr:指向缓冲区写入数据结尾的下一个位置,只相对于本次的写入操作,如果改变了文件指针指向的位置则此指针就回到了缓冲区起始地址。
    int  _cnt: 当前缓冲区的相对位置,就是缓冲区相对于文件的位置,如果文件很大,那么缓冲区的内容可能是文件的中间的一部分,为了正确的将数据写回文件,就需要记录缓冲区在文件的位置,这样就可以准确的写回文件。
    char *_base:指向文件在文件流中的起始内存地址。因为文件读写是以流的形式,而内存缓冲区则只保存文件的其中一小块,记录好,然后读写文件这一部分。
    int _flag :文件状态标志,不同的文件读写模式,以及写入文件后对文件指针指向位置的改变,都会设置这个标志。
    int _file :文件的有效性验证
    int _charbuf:系统为文件读写分配内部缓冲区的状态,如果分配缓冲区失败,则无法读写文件。0表示成功分配,可以正常操作。
    int _bufsiz:内部缓冲区的大小,以字节为单位。与具体的编译器有关。VC编译器分配的是一个内存页的大小即4KB,4096字节。
    char *_tmpfname:临时文件名

    因为有的文件很大,读写时不可能一次性将文件都加载到内存,因此出现了流的思想。就是说,将一个大文件当做一个线性的文件字节流,从文件头一直到文件尾,作为一个流。那么每次读写只操作其中的一小段,操作完毕后再操作下一段。操作完就写回去,段的大小就是内部缓冲区的大小。一般都是写满缓冲区就自动写回文件,但是有时候需要立即写回就需要刷新缓冲,强制将缓冲区的内容写到文件中。这样就很好地解决了内存瓶颈,可以操作任意大小的文件。这个和虚拟内存相似,虚拟内存也是解决内存瓶颈的方案,虚拟内存是针对程序运行内存不够加载整个程序而发展出来的。而对每个段的操作,就需要通过_cnt成员定位缓冲区在文件的位置,以正确的将数据写回文件。而_ptr则是当前缓冲区的文件指针指向的位置。如果缓冲区比文件大,则_ptr和_base是一样的,_cnt则始终为0._base是文件的起始位置,是不会变的,_ptr随着文件的读写会改变。
    上述详细分析了FILE结构体和文件操作机制,而文件操作函数在内部会自动的操作FILE结构体,程序员不用管理这个结构体,也不要去操作,很可能会带来严重问题。在深刻理解这个基础上,再使用文件操作函数,会用一种“一览众山小”的感觉,那么你就可以轻松驾驭文件操作了。