кто освободил буфер setvbuf? - PullRequest
7 голосов
/ 20 апреля 2010

Итак, я покопался в том, как реализована stdio часть libc, и натолкнулся на другой вопрос. Глядя на man setvbuf я вижу следующее:

Когда происходит первая операция ввода-вывода вызывается файл malloc (3), а буфер получен.

Это имеет смысл, ваша программа не должна иметь malloc в ней для ввода / вывода, если вы фактически не используете ее. Моя внутренняя реакция на это заключается в том, что libc уберет здесь свой собственный беспорядок. Я могу только предположить, что это происходит, потому что valgrind не сообщает об утечках памяти (они, конечно, могли бы сделать что-то грязное и не выделять это напрямую через malloc ... но мы будем предполагать, что сейчас оно буквально использует malloc).

Но , вы также можете указать свой собственный буфер ...

int main() {
    char *p = malloc(100);
    setvbuf(stdio, p, _IOFBF, 100);
    puts("hello world");
}

О нет, утечка памяти! Валгринд подтверждает это. Поэтому кажется, что всякий раз, когда stdio выделяет буфер самостоятельно, он автоматически удаляется (самое позднее при выходе из программы, но, возможно, при закрытии потока). Но если вы укажете буфер явно, вы должны очистить его самостоятельно.

Хотя есть одна загвоздка. На странице руководства также написано:

Вы должны убедиться, что пространство, которое Буф указывает на все еще существует к тому времени поток закрыт, что тоже бывает при завершении программы. Например, следующее недействительно:

Теперь это становится интересным для стандартных потоков. Как правильно очистить выделенный вручную буфер, поскольку они закрываются при завершении программы? Я мог бы представить себе «очистить это, когда я закрываю флаг» внутри структуры файла, но это становится проблематичным, потому что, если я читаю это правильно, делая что-то вроде этого:

setvbuf(stdout, 0, _IOFBF, 0);
printf("hello ");
setvbuf(stdout, 0, _IOLBF, 0);
printf("world\n");

вызовет 2 выделения стандартной библиотекой из-за этого предложения:

Если аргумент buf равен NULL, только режим влияет; новый буфер будет выделяется при следующем чтении или записи работа.

РЕДАКТИРОВАТЬ: дополнение к моему вопросу. Поскольку ясно, что я должен free любые буферы, которые я передаю setvbuf, если я действительно использую его на stdout, есть ли практический способ free? Он должен жить до конца программы. Лучшее, что я могу придумать, - это fclose(stdout), затем освободить его или использовать статический буфер, как упоминали некоторые люди. Я спрашиваю, потому что это кажется неуклюжим дизайнерским решением.

Ответы [ 3 ]

3 голосов
/ 20 апреля 2010

По крайней мере, в соответствии со стандартом C, ваш последний сценарий просто не разрешен: «Функция setvbuf может использоваться только после того, как поток, на который указывает поток, был связан с открытым файлом и перед любой другой операцией (кроме в потоке выполняется неудачный вызов setvbuf). (C99, §7.19.5.6 / 2).

Что касается того, когда освобождать память в более простых случаях, по пути следует вызвать atexit(), чтобы зарегистрировать обратный вызов, который освободит память после выхода из main(), но до того, как управление вернется в ОС.

3 голосов
/ 20 апреля 2010

Также со справочной страницы (по крайней мере, в моей системе):

Если buf не равен NULL, вызывающий объект обязан освободить (3) этот буфер после закрытия потока.

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

Перед выходом вы можете сами закрыть потоки, что позволит вам освободить буфер. В качестве альтернативы вы можете очистить потоки и снова вызвать setvbuf с аргументом буфера NULL, чтобы переключиться обратно в буфер, управляемый библиотекой, или небуферизованный ввод / вывод.

1 голос
/ 20 апреля 2010

Вы можете явно закрыть stdin, stdout и stderr (с помощью fclose()).

В большинстве операционных систем память кучи автоматически освобождается при выходе из программы. Таким образом, нет практической проблемы с наличием невыпущенных буферов (есть косметическая проблема, заключающаяся в том, что эти невыпущенные буферы загрязняют вывод Valgrind). Мой совет - использовать статические буферы, если вы чувствуете необходимость вызвать setvbuf() на стандартном вводе или выводе. Статические буферы не нужно выделять или освобождать, и они уместны здесь, так как есть только три стандартных потока, и вы беспокоитесь о ситуации, когда эти потоки остаются открытыми до завершения программы.

...