C语言中使用fseek相比连续调用fread读取文件有什么优势?
C语言文件随机读取问题解答
两种读取方式的实际性能差异
首先给明确结论:在读取二进制文件的指定位置数据时,fseek定位后单次读取的方案,实际性能要远好于连续调用i次fread跳过前面内容的方案,只要是存储在本地磁盘的普通文件,都优先用fseek的实现。
两者的核心差异点:
- 多余数据拷贝开销:连续读的方案哪怕C标准库自带IO缓冲,也需要把中间i-1个不需要的int值从内核缓冲区拷贝到用户态,之后直接丢弃,属于纯无用开销,i越大浪费的内存和CPU资源越多
- 系统调用开销:连续i次
fread会触发更多次用户态到内核态的切换,哪怕每次读的数据很小,切换本身的开销也比fseek+单次fread的两次系统调用高得多 - 极端场景差距明显:如果要读取的是第100万个int值,连续读方案相当于要先遍历4MB左右的无用数据(按int占4字节计算),
fseek方案完全没有这部分开销。
fseek无需读取中间数据的原理
fseek的实现确实和操作系统、C标准库有关,但核心逻辑是通用的,仅适用于支持随机访问的普通文件(管道、socket、终端这些流式文件不支持fseek,调用会直接返回错误):
- 用户态层面:
fseek调用本身不会触发任何磁盘IO操作,它只会修改C标准库为文件流维护的「当前偏移量」变量,同时如果流缓冲区里有未写入的脏数据,会先刷到磁盘,这一步全程是内存操作,没有额外开销。 - 内核层面:后续调用
fread触发实际读操作时,内核会通过文件系统维护的文件块映射表,直接根据要读取的逻辑偏移量,计算出对应数据在磁盘上的物理存储地址,完全不需要读取中间的文件内容。
小补充:所有现代文件系统都会为每个文件维护逻辑偏移到物理磁盘地址的映射关系,相当于给每个文件做了索引,只要给一个偏移值就能直接查到对应位置,不需要从头遍历文件。
- 硬件层面:不管是HDD还是SSD都支持随机访问,HDD可以直接移动磁头到目标磁道读取,SSD可以直接访问任意地址的闪存单元,都不需要顺序读取前面的存储单元。
实现注意事项
- 二进制文件的偏移计算要准确,正确写法是
fseek(fp, i * sizeof(int), SEEK_SET),这里的i从0开始计数,第1个int对应i=0,不要搞错偏移基准。 - 不要用
fseek定位文本文件的读取位置,不同系统的换行符有转义逻辑,文本的字符偏移和实际字节偏移可能不对应,容易读错位置。 - 如果i很小(比如i=2),两种方案的性能差异感知不明显,但还是优先用
fseek,代码可读性更高,也避免后续i变大时出现性能问题。
内容的提问来源于stack exchange,提问作者Zaratruta
相关产品推荐
相关产品推荐

