«правильная» семантика для 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

Говорит:

«Текущая позиция неявно обновляется операциями ввода-вывода. Она может быть явно обновлена ​​с помощью fseek (3) и определена с помощью ftell (3)».

На других справочных страницах упоминается, что ftell может не работать с вещами, которые на самом деле не являются файлами. Тем не менее, я считаю, что они действительно имеют в виду устройства.


person Bruce Adams    schedule 14.09.2017    source источник
comment
Итак, что вам говорит единственный авторитетный ресурс о стандартных функциях?   -  person too honest for this site    schedule 15.09.2017
comment
Похоже на ошибку — зарегистрируйте ее здесь   -  person o11c    schedule 15.09.2017
comment
Хотя вряд ли это проблема, случайное преобразование целочисленного типа, сделанное здесь, добавляет ненужные вопросы к сообщению. Предложите использовать правильный тип и спецификаторы печати. ftell() возвращает long, а size_t требуется "zu".   -  person chux - Reinstate Monica    schedule 15.09.2017
comment
@Olaf есть два авторитетных источника C и Posix. Я считаю, что оба оставляют это неопределенным, но я не языковой юрист. Отсюда мой вопрос.   -  person Bruce Adams    schedule 15.09.2017
comment
@ o11c Я считаю, что ты прав. Это либо ошибка в реализации (мне кажется), либо ошибка в документации, либо и то, и другое. Я отправил отчет об ошибке здесь: sourceware.org/bugzilla/show_bug.cgi?id =22140 Посмотрим, что скажут сопровождающие.   -  person Bruce Adams    schedule 15.09.2017
comment
Также может быть связанная ошибка в самой спецификации Posix austingroupbugs.net/view.php?id= 1156   -  person Bruce Adams    schedule 15.09.2017
comment
есть два авторитетных источника C и Posix — № POSIX относится к ISO9899. Следовательно, он наследует стандарт C. И это понятно (если только вы не забыли соответствующую информацию в вопросе). Прочтите, подробности важны.   -  person too honest for this site    schedule 15.09.2017
comment
Потоки памяти являются частью Posix, а не C. Стандарт C open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf ничего не говорит о ftell(), за исключением предупреждения о разнице между двоичными и текстовыми файлами. Если вы думаете, что это ясно, пожалуйста, объясните. Аналогичным образом pubs.opengroup.org/onlinepubs/9699919799/functions/ обсуждает ищите поведение, но не говорите.   -  person Bruce Adams    schedule 19.09.2017


Ответы (1)


Только что нашел эту цитату в сети в обсуждении:

В документе ftell() Open Group Base Specifications Issue 7 указано, что «ftell() должна возвращать текущее значение индикатора позиции файла для потока». Индикатор позиции файла не обновляется без промежуточного вызова функции fflush. или к функции позиционирования файла (fseek, fsetpos или перемотка назад), или пока буфер не заполнится.

Итак, похоже, что есть некоторая разница в обработке буфера в rh и suse. Вам нужно будет очистить буфер, чтобы прочитать правильную позицию в файле.

person Serge    schedule 15.09.2017
comment
Я хорошо знаю это требование. Обратите внимание, что в моем исходном примере есть вызов fflush после записи. - person Bruce Adams; 15.09.2017
comment
Я нашел это где-то в грязи, это не очень хорошее начало для ответа. Это не отвечает, разрешено это делать или нет (так и есть). - person too honest for this site; 15.09.2017
comment
@Olaf да, я знаю, но это указывает на документ и объясняет поведение. - person Serge; 15.09.2017
comment
Я не вижу никакого указателя. И это тоже неверно/вводит в заблуждение. Читайте стандартные, а не малопонятные форумы! - person too honest for this site; 15.09.2017
comment
@Olaf, вы можете предоставить цитату из стандарта. пожалуйста к этому. - person Serge; 15.09.2017
comment
Как насчет того, чтобы вы сами прочитали 7.21.9.4? Я не за то, чтобы кормить с ложки; собственное исследование позволит гораздо легче получать знания и лучше закреплять их в мозгу. - person too honest for this site; 15.09.2017
comment
@Olaf, к вашему сведению, в стандарте нет абсолютно ничего, что могло бы пролить свет на поведение функции ftell, как в исходном вопросе. - person Serge; 15.09.2017
comment
разница между двумя такими возвращаемыми значениями не обязательно является значимой мерой количества записанных или прочитанных символов. И ftell возвращает long int, а не int. На самом деле компилятор должен выдавать предупреждение об усечении, по крайней мере, для 64-битного Linux. - person too honest for this site; 16.09.2017