Почему printf не сбрасывается после вызова, если в строке формата не указана новая строка?
Почему printf
не скрывается после вызова, если в строке формата не указана новая строка? Это поведение POSIX? Как я могу printf
немедленно очищаться каждый раз?
Ответы
Ответ 1
Поток stdout
по умолчанию буферизован строкой, поэтому он будет отображать только то, что находится в буфере, после того, как он достигнет новой строки (или когда ему будет сказано). У вас есть несколько вариантов немедленной печати:
Напечатайте в stderr
вместо этого, используя fprintf
(stderr
по умолчанию не буферизован):
fprintf(stderr, "I will be printed immediately");
Сбрасывайте стандартный вывод всякий раз, когда он вам нужен, используя fflush
:
printf("Buffered, will be flushed");
fflush(stdout); // Will now print everything in the stdout buffer
Изменить: из комментария Энди Росса, приведенного ниже, вы также можете отключить буферизацию на стандартном выводе с помощью setbuf
:
setbuf(stdout, NULL);
Ответ 2
Нет, это не поведение POSIX, это поведение ISO (ну, это поведение POSIX, но только постольку, поскольку они соответствуют ISO).
Стандартный вывод буферизируется по строке, если он может быть обнаружен для обращения к интерактивному устройству, иначе он полностью буферизуется. Таким образом, есть ситуации, когда printf
не будет скрываться, даже если он получает новую строку для отправки, например:
myprog >myfile.txt
Это имеет смысл для эффективности, поскольку, если вы взаимодействуете с пользователем, они, вероятно, хотят видеть каждую строку. Если вы отправляете вывод в файл, скорее всего, нет другого пользователя на другом конце (хотя это и не невозможно, они могут быть хвостом файла). Теперь вы можете утверждать, что пользователь хочет видеть каждого персонажа, но есть две проблемы с этим.
Во-первых, это не очень эффективно. Во-вторых, первоначальный мандат ANSI C заключался в том, чтобы в первую очередь кодифицировать существующее поведение, а не изобретать новое поведение, и эти проектные решения были приняты задолго до начала процесса ANSI. Даже ISO в настоящее время очень осторожно протекает при изменении существующих правил в стандартах.
Что касается того, как с этим бороться, если вы fflush (stdout)
после каждого выходного вызова, который хотите увидеть сразу, это решит проблему.
В качестве альтернативы вы можете использовать setvbuf
перед тем, как работать с stdout
, чтобы установить его на небуферизованный, и вам не придется беспокоиться о добавлении всех этих строк fflush
в ваш код:
setvbuf (stdout, NULL, _IONBF, BUFSIZ);
Просто имейте в виду, что это может сильно повлиять на производительность, если вы отправляете вывод в файл. Также имейте в виду, что поддержка для этого определяется реализацией, не гарантируемой стандартом.
Раздел ISO C99 7.19.3/3
- это соответствующий бит:
Когда поток небуферизован, символы должны появляться как из источника, так и в пункте назначения как можно скорее. В противном случае символы могут накапливаться и передаваться в или из среды хоста в виде блока.
Когда поток полностью буферизирован, символы предназначены для передачи в среду или из среды хоста в качестве блока при заполнении буфера.
Когда поток буферизуется в строке, символы предназначены для передачи в среду хоста или из нее как блок, когда встречается символ новой строки.
Кроме того, символы предназначены для передачи в виде блока в среду хоста при заполнении буфера, когда запрос запрашивается в небуферизованном потоке или когда запрос запрашивается в потоке с буферизацией, который требует передачи символов из среды хоста.
Поддержка этих характеристик определяется реализацией и может быть затронута с помощью функций setbuf
и setvbuf
.
Ответ 3
Вероятно, это из-за эффективности, и потому, что если у вас есть несколько программ, записывающих один TTY, таким образом вы не получите символов на чередующейся строке. Поэтому, если вы выдаете программы A и B, вы обычно получаете:
program A output
program B output
program B output
program A output
program B output
Это воняет, но это лучше, чем
proprogrgraam m AB ououtputputt
prproogrgram amB A ououtputtput
program B output
Обратите внимание, что даже не гарантируется сброс на новой строке, поэтому вы должны явно скрыться, если для вас есть вопросы.
Ответ 4
Для немедленного сброса вызова fflush(stdout)
или fflush(NULL)
(NULL
означает сброс всего).
Ответ 5
Примечание. Библиотеки времени выполнения Microsoft не поддерживают буферизацию строк, поэтому printf("will print immediatelly to terminal")
:
http://msdn.microsoft.com/en-us/library/86cebhfs.aspx
Ответ 6
stdout буферизуется, поэтому будет выводиться только после печати новой строки.
Чтобы получить немедленный вывод, выполните следующие действия:
- Печать в stderr.
- Сделать stdout небуферизованным.
Ответ 7
по умолчанию, stdout является строковым буферизированным, stderr не имеет буферизации и файл полностью буферизирован.
Ответ 8
Вы можете fprintf вместо stderr, который небуферизирован. Или вы можете сбросить stdout, когда захотите. Или вы можете установить stdout небуферизованным.
Ответ 9
Используйте setbuf(stdout, NULL);
для отключения буферизации.
Ответ 10
Обычно есть 2 уровня buffering-
1. Кеш ядра буфера (ускоряет чтение/запись)
2. Буферизация в библиотеке ввода/вывода (уменьшает количество системных вызовов)
Давайте возьмем пример fprintf and write()
.
Когда вы вызываете fprintf()
, он не записывается напрямую в файл. Сначала он идет в буфер stdio в памяти программы. Оттуда это записывается в буферный кеш ядра с помощью системного вызова write. Таким образом, один из способов пропустить буфер ввода-вывода - напрямую использовать write(). Другие способы - использование setbuff(stream,NULL)
. Это устанавливает режим буферизации на отсутствие буферизации, и данные напрямую записываются в буфер ядра.
Чтобы принудительно переместить данные в буфер ядра, мы можем использовать "\n", который в случае режима буферизации по умолчанию "линейной буферизации" очистит буфер ввода-вывода.
Или мы можем использовать fflush(FILE *stream)
.
Теперь мы находимся в буфере ядра. Ядро (/OS) хочет минимизировать время доступа к диску и, следовательно, читает/записывает только блоки диска. Поэтому, когда выдается read()
, который является системным вызовом и может быть вызван напрямую или через fscanf()
, ядро считывает блок диска с диска и сохраняет его в буфере. После этого данные копируются отсюда в пространство пользователя.
Аналогично, данные fprintf()
, полученные из буфера ввода/вывода, записываются на диск ядром. Это делает read() write() быстрее.
Теперь, чтобы заставить ядро запустить write()
, после чего передача данных контролируется аппаратными контроллерами, есть также несколько способов. Мы можем использовать O_SYNC
или аналогичные флаги во время записи вызовов. Или мы могли бы использовать другие функции, такие как fsync(),fdatasync(),sync()
, чтобы заставить ядро инициировать запись, как только данные будут доступны в буфере ядра.