ftell(FILE * fd)和lseek(int fd,off_t offset,int whence)的结果之间的差异

考虑这个代码示例:

#include  #include  #include  int main() { //this file exists and contains data: "ABCDEFGHIJKLM" FILE* file = fopen("file.txt", "r"); char data[4]; long int pos = ftell(file); fseek(file, 0, SEEK_SET); fread(data, 4, 1, file); fseek(file, pos, SEEK_SET); printf("ftell: %d\n", ftell(file)); printf("lseek: %d\n", lseek(fileno(file), 0, SEEK_CUR)); fread(data, 1, 4, file); //this correctly prints A //but external function needs fileno(file) that has wrong pos printf("%c\n", data[0]); fclose(file); return 0; } 

该计划的结果令人惊讶:

 ftell: 0 lseek: 14 A 

我正在尝试修复我的应用程序中的错误,但我确定前一段时间此程序的结果应为0, 0 (换句话说,从来没有在我的应用程序中出现此错误)。

libc改变了这种奇怪的情况发生了什么?

我怎么能以良好的方式解决这个问题?

lseek(fileno(file), 0, SEEK_SET)是一个很好的解决方案吗?

使用标准库文件操作(例如fread(3)fseek(3) )以及低级系统调用(例如read(2)lseek(3) )是危险的。

这是有问题的原因是因为标准库将缓冲事物,而不是立即将它们写入(到文件描述符)(取决于缓冲模式)。

如果需要访问基础文件描述符,则应确保在获取其fileno之前fflush流。 我在头文件中抛出了这样的东西:

 /** * Safely get the file descriptor associated with FILE, * by fflush()ing its contents first. */ static inline int safe_fileno(FILE *f) { fflush(f); return fileno(f); } 

此外,一旦你调用它,你可能不应该再回到使用FILE*了,因为你已经改变了内核文件指针,但是标准库可能会认为它没有改变(因为你最后使用它)。 正如评论中提到的 ,您可以将FILE*与文件描述符重新同步,如下所示:

 /** * Re-synchronize the file offset of a FILE with the * file offset of its underlying file descriptor. */ static inline void fresync(FILE *f) { off_t off = lseek(fileno(f), 0, SEEK_CUR); fseek(f, off, SEEK_SET); }