在内存流上使用时,谁能解释ftell()的“正确”语义。
给定以下程序:
#include <stdio.h>
#include <stdlib.h>
#include <gnu/libc-version.h>
int main(void)
{
puts (gnu_get_libc_version ());
size_t n_buffer = 1024;
char *buffer = calloc(n_buffer, sizeof(char));
FILE *file = fmemopen(buffer, n_buffer, "w");
/* "ABCD" */
static const char magic_number[] =
{
0x41, 0x42, 0x43, 0x44
};
const size_t written = fwrite(magic_number, 1, 4, file);
fprintf(stderr,"written=%d\n",written);
int fstatus = fflush(file);
fprintf(stderr,"fstatus=%d\n",fstatus);
long ftellpos = ftell(file);
fprintf(stderr,"ftellpos=%ld\n",ftellpos);
fstatus = fseek(file, 0, SEEK_END);
fprintf(stderr,"fstatus=%d\n",fstatus);
ftellpos = ftell(file);
fprintf(stderr,"ftellpos2=%ld\n",ftellpos);
return 0;
}
RHEL7上的输出是:
2.17
written=4
fstatus=0
ftellpos=4
fstatus=0
ftellpos2=4
而OpenSUSE Leap 42的输出为:
2.22
written=4
fstatus=0
ftellpos=0
fstatus=0
ftellpos2=4
(这导致我正在查看的代码中的单元测试失败)
我的问题是:
(标准)需要fseek()来使ftell()的结果有效吗?
这是glibc的错误还是行为改变?
为什么在OpenSUSE上不起作用?
最明显的实现是将文件位置指示器设置为
给fmemopen的内存缓冲区中的索引。
很难看出这怎么可能出问题。
的确实现:
https://github.com/bminor/glibc/blob/73dfd088936b9237599e4ab737c7ae2ea7d710e1/libio/fmemopen.c
有c-> pos = pos + s;在第85行。
大概ftell()只是返回c-> pos(以回旋方式)
在2.17和2.22之间对glibc源进行了一些重新组织
如果我能解开它,那可能会解释这一点。
但这是错误还是功能?
我不确定Posix和C标准是否完全指定了ftell
应该可以正常工作的内存流。
凭直觉很难理解为什么不应该强制要求它
应该工作。
http://man7.org/linux/man-pages/man3/fmemopen.3.html
说:
“当前位置由I / O操作隐式更新。
可以使用fseek(3)显式更新它,并使用ftell(3)确定它。”
其他手册页提到ftell可能不必工作
对于不是真正文件的东西。
但是,我相信他们确实在考虑设备。
最佳答案
刚刚在讨论中在网上找到了这句话:
ftell()开放组基本规范第7期doc状态为“ ftell()应返回流的文件位置指示符的当前值。”如果没有中间调用fflush函数或调用fflush函数,则不会更新文件位置指示符。文件定位功能(fseek,fsetpos或快退),或直到缓冲区已满。
因此,rh和suse似乎在缓冲区处理方面有所不同。您需要以某种方式刷新缓冲区以读取文件中的正确位置。
关于c - ftell()在内存流上使用时的“正确”语义,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/46229457/