问题
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 fflush
ed 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