Ожидаемое поведение freopen () в отношении буферизации (setvbuf ())? - PullRequest
5 голосов
/ 07 февраля 2020

В попытке реализовать freopen () я натолкнулся на часть спецификации в стандарте, которая, насколько я вижу, фактически ничего не указывает.

Итак ... freopen() закроет поток (игнорируя ошибки), очистит его ошибку и флаг EOF, сбросит широкую ориентацию, а затем снова откроет поток в заданном режиме. Это очень ясно; это в основном fclose () / fopen (). Даже если это не определено таким образом, довольно ясно, что это было то, что было задумано.

Однако у меня есть два вопроса относительно того, что setvbuf() могло бы сделать с настройкой потока пользовательский буфер и / или изменение политики буфера.


Вопрос 1.

1) Ожидается ли freopen() возврат к значениям по умолчанию состояние, как если бы оно на самом деле называется fopen()? Или ожидается, что он перенесет в новый поток все, что пользователь установил с помощью setvbuf() на старом? Это относится как к буферной памяти, так и к политике буфера, но основная проблема здесь связана с буферной памятью.

Спецификация для fclose() указывает, что любой буфер, связанный с потоком через setvbuf(), который пользователь связал с потоком, т.е. теперь может быть free() 'd пользователем.

Но freopen() указывает только то, что закрывает файл, связанный с потоком, но не fclose() его.

Итак, после freopen() остается ли связанная с пользователем буферная память связанной с потоком?


Вопрос 2.

freopen() предположительно может использоваться в FILE структуре, которая на самом деле не связана с открытым файлом во время вызова (поскольку ошибки при попытке закрыть файл должны игнорироваться).

Эта файловая структура может ранее был открытый поток с назначенной пользователем буферной памятью и политикой буфера. freopen() для соблюдения этих настроек, т. Е. Повторно связать буферную память / политику с "re" -открытым файлом, или это для переустановки структуры по умолчанию, предполагая, что пользователь free() d буферной памяти после fclose() файл ранее?


Мое взятие.

Глядя на Q2, я не вижу способа для стандартной библиотеки надежно определить, является ли не открытая в настоящее время структура FILE с выделенной пользователем буферной памятью все еще «владеет» этой буферной памятью, или же пользователь уже освободил эту память. (Возможно, эта память может быть локальной, то есть не входить в списки памяти, обрабатываемые malloc() / free(), даже если бы я был готов go там - и это было бы весьма нехарактерно сложной работой, ожидаемой от функций стандартной библиотеки .)

Аналогичные соображения для политики буфера.

Так что, насколько я вижу, единственный надежный способ обработки вещей - это freopen() для обработки закрытие «любого файла, связанного с указанным потоком» как «реального» * ​​1064 *, и переустановка буферной памяти / policyto default.

Прав ли я в этом понимании, или есть альтернатива? ответ на Q1 / Q2?

1 Ответ

2 голосов
/ 07 февраля 2020

Стандарт C не устанавливает, что состояние буферизации изменяется каким-либо образом.

Вся C11 freopen() спецификация есть (включая сноску 272 ):

7.21.5.4 Функция freopen

Сводка

1

     #include <stdio.h>
     FILE *freopen(const char * restrict filename,
          const char * restrict mode,
          FILE * restrict stream);

Описание

2 Функция freopen открывает файл, имя которого является строкой, на которую указывает filename, и связывает с ней поток, на который указывает поток. Аргумент mode используется так же, как и в функции fopen. 272)

3 Если filename равно нулю указатель, функция freopen пытается изменить режим потока на режим, указанный mode, как если бы использовалось имя файла, связанного в данный момент с потоком. Это определяется реализацией, какие изменения режима разрешены (если есть) и при каких обстоятельствах.

4 Функция freopen сначала пытается закрыть любой файл, связанный с указанным потоком. Неспособность закрыть файл игнорируется. Индикаторы ошибок и конца файла для потока очищаются.

Возвращает

5 freopen функция возвращает нулевой указатель, если операция открытия завершается неудачно. В противном случае freopen возвращает значение stream.


272) Основное использование функции freopen - изменение файла, связанного с стандартный текстовый поток (stderr, stdin или stdout), поскольку эти идентификаторы не должны быть изменяемыми l-значениями, которым может быть присвоено значение, возвращаемое функцией fopen.

Для меня ключевая фраза: связывает с ним поток, на который указывает поток. Существующий поток , на который указывает stream, имеет новый файл связанный с этим - и все. Не указывая никаких изменений в буферизации, это подразумевает, что текущее состояние буфера сохраняется, поскольку freopen() просто связывает новый файл и режим с существующим потоком . Насколько я понимаю, должны быть сделаны только те изменения в потоке FILE *, которые явно указаны в стандарте.

Обратите также внимание на параграф 4: Функция freopen сначала пытается закрыть любой файл, который связанный с указанным потоком. Опять же, стандарт относится к указанному потоку .

Для меня вывод кажется неизбежным: freopen() не создает новый поток. Он просто указывает существующему потоку на новый файл - и это все, что он делает.

Это чтение - то, что состояние буфера текущего потока не изменено - поддерживается текущими реализациями. Они не изменяют состояние буферизации существующего потока.

Ни реализация GLIB C freopen() , ни реализация OpenSolaris / Illumos (очень вероятно, что будь текущая реализация Solaris), похоже, изменяет состояние исходной буферизации, кроме очистки любого буфера перед закрытием файла.

Функция freopen(), по-видимому, плохо указана. POSIX имеет следующее: :

ИСПОЛЬЗОВАНИЕ ПРИЛОЖЕНИЯ

Функция freopen() обычно используется для присоединения предварительно открытых потоков связанные с stdin, stdout и stderr с другими файлами.

Поскольку реализации не требуют поддержки каких-либо изменений режима потока, когда аргумент pathname равен NULL, переносимые приложения не могут полагаться при использовании freopen() для изменения режима потока, и использование этой функции не рекомендуется. Первоначально эта функция была добавлена ​​в стандарт ISO C, чтобы упростить перевод stdin и stdout в двоичный режим. Поскольку символ 'b' в режиме не влияет на системы POSIX, такое использование этой функции не требуется в приложениях POSIX. Однако, несмотря на то, что 'b' игнорируется, успешный вызов freopen (NULL, "wb", stdout) оказывает влияние. В частности, для обычных файлов он усекает файл и устанавливает индикатор позиции файла для потока в начале файла. Возможно, что эти побочные эффекты являются непреднамеренным следствием того, как эта функция указана в стандарте ISO / IEC 9899: 1999, но до тех пор, пока не будет изменен стандарт ISO C, приложения, которые успешно вызов freopen (NULL, "wb", stdout) будет вести себя непредвиденным образом в соответствующих системах в таких ситуациях, как:

{ appl file1; appl file2; } > file3

, что приведет к file3 , содержащему только выходные данные второго вызова appl .

...