c - ftell(FILE* fd)和lseek(int fd, off_t offset, int where)结果



考虑以下代码示例:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
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: %dn", ftell(file));
    printf("lseek: %dn", 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("%cn", 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*,因为你已经改变了内核文件指针,但是标准库可能会认为它没有改变(因为你上次fflush修改了它)。正如在评论中提到的,您可以像这样重新同步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);
}

最新更新