Почему 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(), чтобы заставить ядро инициировать запись, как только данные будут доступны в буфере ядра.