fread
定义
fread是C标准库中定义在stdio.h里的库函数,其原型为size_t fread(void *restrict ptr, size_t size, size_t nmemb, FILE *restrict stream)[1]。按POSIX规范的解释,它从stream所指向的流中最多读入nitems个元素,每个元素的长度由size指定,读出的字节依次写入ptr所指的数组[1]。函数名本身来自"file read"的缩写,语义上对应标准库的成对写函数fwrite[2]。
定义的第二段可以说明参数含义与返回值:buffer指向接收数据的存储区,size是单个数据项的字节数,count为最多读取的项数,stream为指向FILE结构的指针[4]。返回值为成功读入的完整项数,当size或count为0时返回0且缓冲区内容不变[4]。
原理
从规范角度看,fread对每个待读对象等效于连续调用size次fgetc,把取到的字节按读入顺序放进恰好覆盖该对象的unsigned char数组,随后将流的文件位置指示器向前推进实际成功读取的字节数[1]。若中途发生错误,位置指示器的取值不作规定;若只读到不完整的一个元素,该元素的值同样不作规定[1]。这一描述等价于下面的关系:读入字节数约为size与返回项数之积。
由于返回值表示的是项数,并非字节数,只有当size等于1时返回值才与实际传输的字节数相等[2]。因此常见写法有两种:以单个字节为单位读取时传size为1、nitems为缓冲区长度;以定长记录为单位读取时把记录长度作为size,把记录条数作为nitems[1]。判断完整性时通常比较返回值与期望项数是否相等,而不能只看是否非零。
函数本身并不说明读到的数据算文件结束还是算出错,两种情况下返回值都会小于请求项数甚至为0,需要继续调用feof与ferror才能判断具体原因[3]。在基于glibc的平台上,fread被标为MT-Safe,即多线程同时调用时不会破坏内部状态[2]。
发展历程
fread与fwrite最早出现在Version 7 AT&T UNIX中,此后随C语言标准化的进程被收入标准库[3]。FreeBSD手册页说明,这两个函数符合ISO/IEC 9899:1990,也就是通常所说的C90标准[3]。
POSIX方面,手册页把fread的规范沿革追溯到POSIX.1-2001,现行版本对应POSIX.1-2008与C11[2]。C99修订后,函数原型中加入了restrict限定符,用于表明缓冲区与流之间不存在重叠[5]。C标准的后续版本继续沿用该接口,cppreference列出的引用覆盖了C89、C99、C11、C17与C23各版条文[5]。
不同实现还在标准函数之外提供了不加锁的变体。FreeBSD提供fread_unlocked,由调用者自行用flockfile锁住流,从而省去每次调用内部加锁的开销,并避免多个线程操作同一流时出现竞争[3]。微软的C运行库则给出_fread_nolock作为对应的非锁定版本[4]。
应用
fread最常见的用途是成批读取二进制文件。以读取二进制文件头为例,可以先用fopen以二进制方式打开文件,再用一次fread把定长头部读入数组,然后检查返回项数是否与请求一致[2]。由于读入的是原始字节,程序可以直接按结构体或数组的内存布局解释这些内容,因此适合读取图像、音频、可执行文件等自带固定格式的数据[5]。
在需要保存和恢复数组的场景中,fread与fwrite构成对称的一对。以双精度数组为例,先用fwrite把整个数组写入以二进制模式打开的文件,再以读模式打开同一文件,用fread配合sizeof回读,只要返回值等于元素个数即表明读取完整[5]。这种写法在科学计算与数据持久化的简单实现中较为常见。
fread也可用于文本流。标准规定它按字节搬运数据,不负责格式解析,需要解析数字或字段时应改用fscanf一类带格式的输入函数[1]。此外,该函数可用在标准输入以及其他由FILE指针表示的流上,并不局限于磁盘文件[1]。
局限
fread按原始字节搬运数据,读出的内容是否可用取决于写入方与读取方对数据布局的一致理解。POSIX明确指出,由于元素长度与字节序可能存在差异,用fwrite写出的文件与具体应用相关,换一个应用或换用不同处理器后未必还能用fread正确读回[1]。跨平台交换数据时通常需要另行规定字节序和字段宽度。
返回值语义也容易引起误用。由于返回的是项数而非字节数,当size大于1时把返回值直接当作长度使用会低估实际读到的数据量[2]。同时,fread不区分文件结束与读取错误,若只根据返回值是否为零来判断,可能把真实的I/O故障当成正常结束[3]。
在文本模式下,实现可能对换行符做转换。微软文档说明,若流以文本模式打开,Windows风格的回车换行对会被替换为单个换行符,且这一替换不影响文件指针与返回值[4]。该文档还提示,当请求量不小于内部缓冲区(默认4096字节,可用setvbuf调整)时数据会被直接复制到用户缓冲区,转换后缓冲区中超出返回长度部分可能残留未转换内容,若要把缓冲区当作C字符串使用,需自行补上结束符[4]。
此外,标准fread默认对流的加锁会带来额外开销,高并发读同一流的程序往往改用平台提供的非锁定变体并自行管理同步[3]。发生错误时文件位置指示器的位置不确定,出错后继续在同一流上读取通常不可靠,需要重新定位或重新打开[1]。
参见
参考资料
- fread [引用日期2026-10-29]
- fread, fwrite [引用日期2026-10-29]
- man [引用日期2026-10-29]
- fread [引用日期2026-10-29]
- fread [引用日期2026-10-29]