Difference between result of ftell(FILE* fd) and lseek(int fd, off_t offset, int whence)

余生颓废 提交于 2019-12-11 04:00:46

问题


Consider this sample of code:

#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: %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;
}

Result of this program is surprising:

ftell: 0
lseek: 14
A

I'm trying to fix bug in my application, but I'm sure that some time ago result of this program should be 0, 0 (in other words, never before has there been this error in my app).

What changed in libc that this weird situation occurs?

How I can fix this in good way?

Is lseek(fileno(file), 0, SEEK_SET) a good solution?


回答1:


It is dangerous to use both standard library file operations (e.g. fread(3), fseek(3)) along with low-level system calls (e.g. read(2), lseek(3)).

The reason this is problematic is because the standard library will buffer things, and not actually write them out (to the file descriptor) immediately (depending on the buffering mode).

If you need to access the underlying file descriptor, you should be sure to fflush the stream before getting its fileno. I've thrown something like this in a header file:

/**
 * 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);
}

Also, once you call this, you probably shouldn't go back to using the FILE* again, because you've changed the kernel file pointer, but the standard library may assume it is unchanged (since you last fflushed it). As was mentioned in a comment, you may be able to re-sync the FILE* with the file descriptor like this:

/**
 * 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);
}


来源:https://stackoverflow.com/questions/31687964/difference-between-result-of-ftellfile-fd-and-lseekint-fd-off-t-offset-int

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!