当在内存流上使用时,ftell()的'正确'语义

时间:2017-09-14 23:11:33

标签: c posix stdio ftell

在内存流上使用时,任何人都可以解释ftell()的'正确'语义。

鉴于以下计划:

Utterance 1

RHEL7上的输出是:

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

OpenSUSE Leap 42的输出是:

2.17
written=4
fstatus=0
ftellpos=4
fstatus=0
ftellpos2=4

(这导致我正在查看的代码中的单元测试失败)

我的问题是:

  • fseek()是否需要(通过标准)使ftell()的结果有效?
  • 这是glibc的行为错误或改变吗?
  • 为什么它不适用于OpenSUSE?

最明显的实现是文件位置指示器 给fmemopen的内存缓冲区中的索引。 很难看出这可能出错。

确实是实施:

https://github.com/bminor/glibc/blob/73dfd088936b9237599e4ab737c7ae2ea7d710e1/libio/fmemopen.c

有c-&gt; pos = pos + s;在第85行。

大概ftell()只返回c-&gt; 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可能不必工作 对于那些不是真正文件的东西。 但是,我相信他们在那里确实有设备。

1 个答案:

答案 0 :(得分:0)

在讨论中刚刚在网上找到了这句话:

ftell()打开组基本规范第7期doc状态'ftell()将返回流的文件位置指示符的当前值。'文件位置指示符未更新而没有干预调用fflush功能或文件定位功能(fseek,fsetpos或倒带),或直到缓冲区已满。

因此,看起来rh和suse中存在一些缓冲区处理差异。您需要以某种方式刷新缓冲区以读取文件中的正确位置。