Использование rewind() для ФАЙЛА*, открытого с помощью fmemopen

Решено с помощью glibc 2.24 -- см. ОБНОВЛЕНИЕ ниже

Вот кусок C-кода (скомпилирован с помощью gcc 5.3.1, glibc 2.23):

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>

int main() {
  const char* s1="Original content of file.\n"
                 "still original content and some remaining original content.\n";
  const char* s2="overwriting data  with new content\n";
  const char* s3="appended data\n";

  const size_t bufsz=strlen(s1)+1;
  char buf[bufsz];
  FILE *f;
  int r;

  f=fmemopen(buf,bufsz,"w");
  assert(f!=NULL);

  // setbuf(f, NULL);          // variant no. 1
  // setbuffer(f, buf, bufsz); // variant no. 2

  r=fwrite(s1,strlen(s1),1,f);
  assert(r==1);

  r=fflush(f);
  assert(r==0);

  rewind(f);

  r=fwrite(s2,strlen(s2),1,f);
  assert(r==1);

  r=fwrite(s3,strlen(s3),1,f);
  assert(r==1);

  r=fclose(f);
  assert(r==0);

  printf("%s",buf);
}
  • Он делает то, что я ожидаю. Вывод:

    overwriting data  with new content
    appended data
    and some remaining original content.
    
  • Теперь man-страница fmemopen(3) советует либо отключить буферизацию (раскомментировать вариант 1), либо явно установить buf в качестве буфера (раскомментировать вариант 2).

    Однако в обоих случаях я получаю в результате:

    appended data
    ta  with new content
    ginal content and some remaining original content.
    

    Таким образом, добавленные данные не были записаны после второго содержимого, как ожидалось, а перезаписали второе содержимое.

  • Поведение остается прежним, если я открываю файл в двоичном режиме (т.е. заменяя режим «w» на «wb»).

  • valgrind не сообщает об ошибках (за исключением ложного срабатывания «Перекрытие источника и назначения» в случае буферизации. Последнее связано с попыткой записать в память буфер STDIO, который действительно является одним и тем же адресом.)

Что не так? Я сделал ошибку? Или это ошибка GLIBC?

ОБНОВИТЬ

4 августа вышла новая версия glibc 2.24. С gcc 5.4.0 и glibc 2.24, вариант №. 1 (небуферизованный ФАЙЛ) работает нормально. Вариант 2 (самобуферизованная версия) дает другой, но все же ошибочный результат. Таким образом, я считаю, что R.. прав, утверждая, что это ошибка документации на странице руководства fmemopen(3). Подниму отчет об ошибке...


person ralfg    schedule 09.08.2016    source источник
comment
Мои результаты отличаются. Я получаю ожидаемый результат как для вашего кода, так и для опубликованного и с раскомментированным вариантом 1. (GCC 4.8)   -  person DevSolar    schedule 09.08.2016
comment
Имеет ли значение сброс потока перед перемоткой?   -  person Ian Abbott    schedule 09.08.2016
comment
@Ian Abott: дополнительный fflush(f); непосредственно перед перемоткой не меняет поведение ни в одном из вариантов. Возвращаемое значение fflush(f) равно 0 во всех случаях.   -  person ralfg    schedule 10.08.2016


Ответы (1)


Теперь man-страница fmemopen(3) советует либо отключить буферизацию (раскомментировать вариант 1), либо явно установить buf в качестве буфера (раскомментировать вариант 2).

Последнее, безусловно, является неопределенным поведением. Не делай этого. Мне непонятно, что за текст вы нашли на справочной странице Linux:

В качестве альтернативы вызывающая сторона может явно установить buf в качестве буфера потока stdio, в то же время информируя stdio о размере буфера, используя:

setbuffer(stream, buf, size);

пытается выразить, но это ошибка документации и, вероятно, ее следует просто удалить.

Что касается неработающего режима без буферизации, вероятно, это ошибка glibc. Постарайтесь сделать тестовый пример настолько минимальным, насколько это возможно, протестируйте на недавнем glibc, и если это все еще происходит, отправьте отчет об ошибке в glibc bugzilla:

https://sourceware.org/bugzilla/enter_bug.cgi?product=glibc

Безопасной альтернативой, когда вы хотите немедленно узнать об ошибках, является просто вызов fflush и проверка возвращаемого значения.

person R.. GitHub STOP HELPING ICE    schedule 09.08.2016
comment
Спасибо. Я загрузил только что выпущенный glibc 2.24, и теперь поведение корректно в варианте 1 и по-прежнему неправильно (но по-другому) в варианте 2. - person ralfg; 10.08.2016
comment
Вариант 2 — это ошибка в вашей программе. Это недопустимое использование. Проект справочных страниц уже исправлен, чтобы не делать эту неверную рекомендацию. - person R.. GitHub STOP HELPING ICE; 10.08.2016