8.5实例-fopen和getc函数的实现
标准库中的文件不是通过文件描述符描述的,而是使用文件指针描述的.文件指针是一个指向包含文件各种信息的结构的指针,该结构包含下列内容:一个指向缓冲区的指针,通过它可以一次读入文件的一大块内容;一个记录缓冲区中剩余字符数的计数器;一个指向缓冲区中下一个字符的指针;文件描述符;描述读/写模式的标志;描述错误状态的标志等.
描述文件的数据结构包含在头文件<stdio.h>中,任何需要使用标准输入/输出库中函数的程序都必须在源文件中包含这个头文件,此文件也被库中的其他函数包含.只供标准库中其他函数使用的名字以下划线开始,因此一般不会与用户程序中的名字冲突,所有的标准库函数都遵循该约定.
后面那个一大串的定义真是够复杂的.
从网上摘下来的一段解释.
我们知道,当我们从键盘输入数据的时候,数据并不是直接被我们得到,而是放在了缓冲区中,然后我们从缓冲区中得到我们想要的数据 。如果我们通过setbuf()或setvbuf()函数将缓冲区设置10个字节的大小,而我们从键盘输入了20个字节大小的数据,这样我们输入的前10个数据会放在缓冲区中,因为我们设置的缓冲区的大小只能够装下10个字节大小的数据,装不下20个字节大小的数据。那么剩下的那10个字节大小的数据怎么办呢?暂时放在了输入流中。
再说一下 FILE 结构体中几个相关成员的含义:
cnt 剩余的字符,如果是输入缓冲区,那么就表示缓冲区中还有多少个字符未被读取
ptr 下一个要被读取的字符的地址
base 缓冲区基地址
我们向缓冲区中放入了10个字节大小的数据,FILE结构体中的 cnt 变为了10 ,说明此时缓冲区中有10个字节大小的数据可以读,同时我们假设缓冲区的基地址也就是 base 是0x00428e60 ,它是不变的 ,而此时 ptr 的值也为0x00428e60 ,表示从0x00428e60这个位置开始读取数据,当我们从缓冲区中读取5个数据的时候,cnt 变为了5 ,表示缓冲区还有5个数据可以读,ptr 则变为了0x0042e865表示下次应该从这个位置开始读取缓冲区中的数据 ,如果接下来我们再读取5个数据的时候,cnt 则变为了0 ,表示缓冲区中已经没有任何数据了,ptr 变为了0x0042869表示下次应该从这个位置开始从缓冲区中读取数据,但是此时缓冲区中已经没有任何数据了,所以要将输入流中的剩下的那10个数据放进来,这样缓冲区中又有了10个数据,此时 cnt 变为了10 ,注意了刚才我们讲到 ptr 的值是0x00428e69 ,而当缓冲区中重新放进来数据的时候这个 ptr 的值变为了0x00428e60 ,这是因为当缓冲区中没有任何数据的时候要将 ptr 这个值进行一下刷新,使其指向缓冲区的基地址也就是0x0042e860这个值!因为下次要从这个位置开始读取数据!
在这里有点需要说明:当我们从键盘输入字符串的时候需要敲一下回车键才能够将这个字符串送入到缓冲区中,那么敲入的这个回车键(\r)会被转换为一个换行符\n,这个换行符\n也会被存储在缓冲区中并且被当成一个字符来计算!比如我们在键盘上敲下了123456这个字符串,然后敲一下回车键(\r)将这个字符串送入了缓冲区中,那么此时缓冲区中的字节个数是7 ,而不是6。
缓冲区的刷新就是将指针 ptr 变为缓冲区的基地址 ,同时 cnt 的值变为0 ,因为缓冲区刷新后里面是没有数据的
然后看一下下面的代码.这这是一个简易版本,了解即可.
首先确定输入的参数是否符合要求.
然后在_iob结构数组中查找空位.
_iob数组的每个元素都是一个FILE类型的结构.
所以遍历数组元素中FILE类型的flag成员,判断是否为空.就是flag与_READ和_WRITE进行与操作后不等于0.(因为flag是空着(这个元素没有进行读或写操作)的话就是0而0对于任何二进制的与操作都是0)
然后如果fp这个结构指针超过了_iob数组的范围,那么就是没有位置.因为如果有位置之前的循环就会跳出从而不会超过_iob数组的范围.
然后就是依照参数的类型(r,w,a)去执行相应的操作.
如果是w就新创建一个文件..
如果是a就先打开这个文件,如果发生错误那就创建这个文件.然后用lseek去将写入的位置移动到末尾.
否则就只是打开该文件即可.
然后将_iob这个保存已打开文件数组的该文件位置的内容初始化.
然后看一下_fillbuf函数的内容.
因为_fillbuf只针对读取进行缓冲操作.所以如果不是读取将会直接退出函数并返回EOF.
然后判断是否需要缓冲.,如果不需要则将缓冲大小设为1,实际上也就没有了缓冲.来一个走一个.
然后如果参数这个文件还未分配缓冲区,则继续判断是否有足够空间或没有错误的分配空间进行缓存.
然后将缓冲区的开头给ptr这个指针,由ptr进行遍历缓冲区操作.
然后通过read读取文件fd的bufsize个字符保存到ptr指向的缓冲区中.然后将读取的数量给cnt进行保存.
然后判断文件是否到末尾或者发生了错误.
正常情况下到末尾cnt都是0,然后先自减的话就是-1,就是正常结束.如果其他情况就是错误的.
然后返回ptr指向的缓冲区中的字符.
下面那个定义也懒得说了.
然后是练习8-2.因为这个练习基本没什么变化,而且改变之后的运行速度和代码的长短都有所退步,所以在这随便写一下就好了.
总体就是将之前flag的位操作换成了对其中的各个成员进行对比.也没什么需要说的.暂时这样.