必须用二进制模式(如"rb+")打开文件,文本模式下fseek/ftell行为不可靠;whence参数按需选SEEK_SET/SEEK_CUR/SEEK_END;ftell值需结合自定义数据布局使用,不可直接作数组下标;C++流与C函数混用会不同步,应避免。

用 fseek 定位失败,ftell 返回 -1 怎么办
常见错误是没检查文件打开模式——fseek 和 ftell 在文本模式下行为不可靠,尤其 Windows 上换行符转换会干扰字节偏移。必须用二进制模式打开:"rb+" 或 "wb+"(带 + 才支持读写)。如果只开 "r" 或 "w",fseek 可能静默失败,ftell 返回 -1。
-
fopen("data.bin", "r")→ 不支持写,fseek后fwrite会失败 -
fopen("data.bin", "w+")→ 清空原文件,慎用于随机修改 -
fopen("data.bin", "rb+")→ 最常用:保留内容、可读可写、定位准确
fseek 的 whence 参数选 SEEK_SET 还是 SEEK_CUR
取决于你要跳到哪:想从文件开头算起就用 SEEK_SET;想在当前位置偏移就用 SEEK_CUR;末尾用 SEEK_END(但注意负偏移需文件支持,某些流不适用)。特别注意:SEEK_END + 负偏移常被误用来“倒着读”,但它依赖底层是否允许回溯,标准 C 文件流在管道或 stdin 上会失败。
-
fseek(fp, 1024, SEEK_SET)→ 跳到第 1024 字节(从 0 开始) -
fseek(fp, -8, SEEK_CUR)→ 往回退 8 字节(当前必须不在开头) -
fseek(fp, 0, SEEK_END)配ftell(fp)是获取文件大小的惯用法
为什么 ftell 返回值不能直接当数组下标用
ftell 返回的是当前字节偏移(long 类型),不是行号或记录序号。如果你存的是结构体数组,得自己算对齐和大小——比如 struct Record { int id; char name[32]; };,每个占 36 字节,那第 5 条记录位置就是 5 * 36,不能直接拿 ftell 值除以 36 反推索引,因为中间可能有 fseek 调整过位置,或者写入时没对齐。
- 结构体含指针或浮点数时,跨平台序列化要小心字节序和填充
- 用
fread(&r, sizeof(r), 1, fp)读结构体前,确保fseek已准确定位到该结构体起始 -
ftell值本身无意义,只有配合你自己的数据布局才有业务含义
C++ 里混用 std::fstream 和 fseek/ftell 会出问题吗
会。C++ 流对象(如 std::fstream)内部维护自己的缓冲区和位置指针,与 C 的 FILE* 不同步。一旦你用 fileno(fp) 拿到 fd 再调 fseek,C++ 流的缓存就失效了,后续 read() 可能读错位置或重复/跳过数据。真要混用,必须先 fp->sync_with_stdio(false) 并禁用缓冲,或者干脆别混——要么全用 C 风格,要么全用 C++ 的 seekg/seekp。
立即学习“C++免费学习笔记(深入)”;
-
std::fstream::seekg(1024)等价于fseek(fp, 1024, SEEK_SET) -
std::fstream::tellg()对应ftell,但返回pos_type,不是long - 混合调用后加
fp->clear()和fp->sync()也不能保证安全
文件大小超过 2GB 时,ftell 在 32 位系统上会溢出,fseek 的 offset 参数也受限。这时候得用 _fseeki64 / _ftelli64(Windows)或 fseeko / ftello(POSIX),且编译器需开启大文件支持。










