"правильная" семантика для 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, возможно, не должен работать для вещей, которые на самом деле не являются файлами. Тем не менее, я считаю, что они действительно имеют в виду устройства там.
1 ответ
Просто нашел эту цитату в сети в обсуждении:
Открытая базовая спецификация ftell(), выпуск 7, сообщает, что ftell() должна возвращать текущее значение индикатора положения файла для потока. Индикатор положения файла не обновляется без промежуточного вызова функции fflush или функции позиционирования файла (fseek, fsetpos или rewind) или до тех пор, пока буфер не будет заполнен.
Итак, похоже, что есть некоторая разница в обработке буфера в rh и suse. Вам необходимо очистить буфер таким образом, чтобы прочитать правильную позицию в файле.