c - 在内存流上使用 ftell() 时的“正确”语义

标签 c posix stdio ftell

谁能解释一下 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 期文档指出“ftell() 应返回流的文件位置指示符的当前值。”如果没有中间调用,文件位置指示符不会更新。 fflush 函数或文件定位函数(fseek、fsetpos 或 rewind),或直到缓冲区已满。

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

关于c - 在内存流上使用 ftell() 时的“正确”语义,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46229457/

相关文章:

C程序输出顺序错误的Eclipse

c - 将数组大小声明为 a[N+1]

c++ - 禁用 fork 进程的标准输出缓冲

c - 为什么 scanf 可以读取超过 1024 个字符,而 stdin 流缓冲区只有 1024 个字节?

java - 尽管指定了正确的结构,为什么我会获得无效的内存访问?

找不到自定义头文件

c - getutent 和 Linux 计时器问题

c++ - pthread_mutex_destroy : why is it returning EBUSY?

php - 从 PHP 写入标准输入?

julia - 从 Julia 中捕获和显示输出