在内存流上使用时,谁能解释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/

10-11 15:18