问题描述
请考虑以下代码示例:
#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;
}
该程序的结果令人惊讶:
ftell: 0
lseek: 14
A
我正在尝试修复应用程序中的错误,但我确定该程序的前一段时间应为0, 0
(换句话说,我的应用程序中从来没有这个错误). /p>
这种奇怪的情况在libc中发生了什么变化?
我如何才能很好地解决此问题?
lseek(fileno(file), 0, SEEK_SET)
是一个好的解决方案吗?
同时使用标准库文件操作(例如fread(3)
,fseek(3)
)和低级系统调用(例如read(2)
, lseek(3)
).
这是有问题的,原因是标准库将缓冲内容,而不是立即将它们实际写出(写入文件描述符)(取决于缓冲模式).
如果您需要访问基础文件描述符,则应确保 fflush
流,然后再获取其fileno
.我已经在头文件中抛出了这样的内容:
/**
* 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*
了,因为您已经更改了内核文件指针,但是标准库可能会假定它没有改变(因为您上次使用 ed).如前所述,注释,您也许可以将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);
}
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?
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);
}
这篇关于ftell(FILE * fd)和lseek(int fd,off_t offset,int whence)结果之间的差异的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!