Почему printf не сбрасывается после вызова, если в строке формата нет новой строки?

443

Почему printf не скрывается после вызова, если в строке формата не указана новая строка? Это поведение POSIX? Как я могу printf немедленно очищаться каждый раз?

  • 2
    Вы исследовали, происходит ли это с любым файлом или только с терминалами? это звучит как умная функция терминала, которая не выводит незавершенную строку из фоновой программы, хотя я ожидаю, что она не будет применяться к программе переднего плана.
  • 6
    Под Cygwin Bash я вижу то же самое неправильное поведение, даже если перевод строки находится в строке формата. Эта проблема является новой для Windows 7; тот же исходный код отлично работал на Windows XP. MS cmd.exe мигает, как и ожидалось. Исправление setvbuf(stdout, (char*)NULL, _IONBF, 0) решает эту проблему, но, конечно, не должно быть необходимым. Я использую MSVC ++ 2008 Express. ~~~
Показать ещё 2 комментария
Теги:
printf
flush

9 ответов

545
Лучший ответ

Поток stdout буферизуется, поэтому будет отображаться только то, что в буфере после того, как оно достигнет новой строки (или когда оно было сказано). У вас есть несколько вариантов печати сразу:

Печать в stderr вместо этого с помощью fprintf:

fprintf(stderr, "I will be printed immediately");

Сбрасывать stdout всякий раз, когда вам нужно использовать fflush:

printf("Buffered, will be flushed");
fflush(stdout); // Will now print everything in the stdout buffer

Изменить. Из комментария Энди Росса ниже вы также можете отключить буферизацию на stdout с помощью setbuf:

setbuf(stdout, NULL);
  • 242
    Или полностью отключить буферизацию: setbuf(stdout, NULL);
  • 67
    Кроме того, я просто хотел упомянуть, что, очевидно, в UNIX символ новой строки обычно очищает буфер, только если stdout является терминалом. Если вывод перенаправляется в файл, новая строка не сбрасывается.
Показать ещё 11 комментариев
107

Нет, это не поведение 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.

  • 7
    Я только что натолкнулся на сценарий, в котором даже есть '\ n', printf () не сбрасывается. Это было преодолено добавлением fflush (stdout), как вы упомянули здесь. Но мне интересно, почему '\ n' не удалось очистить буфер в printf ().
  • 9
    @QiangXu, стандартный вывод - это линейная буферизация только в том случае, если можно окончательно определить ссылку на интерактивное устройство. Так, например, если вы перенаправите вывод с помощью myprog >/tmp/tmpfile , это будет полностью буферизовано, а не буферизовано строкой. По памяти определение того, является ли ваш стандартный вывод интерактивным, оставлено на усмотрение реализации.
Показать ещё 1 комментарий
23

Вероятно, это из-за эффективности, и потому, что если у вас есть несколько программ, записывающих один 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

Обратите внимание, что даже не гарантируется сброс на новой строке, поэтому вы должны явно скрыться, если для вас есть вопросы.

19

Для немедленного сброса вызова fflush(stdout) или fflush(NULL) (NULL означает сброс всего).

  • 25
    Имейте в виду, fflush(NULL); обычно очень плохая идея. Это снизит производительность, если у вас будет открыто много файлов, особенно в многопоточной среде, где вы будете бороться со всеми за блокировки.
11

Примечание. Библиотеки времени выполнения Microsoft не поддерживают буферизацию строк, поэтому printf("will print immediatelly to terminal"):

http://msdn.microsoft.com/en-us/library/86cebhfs.aspx

  • 2
    Хуже, чем printf идущий непосредственно к терминалу в «нормальном» случае, является тот факт, что printf и fprintf получают более грубую буферизацию даже в тех случаях, когда их вывод используется для немедленного использования. Если MS не исправит ошибки, одна программа не сможет захватить stderr и stdout из другой и определить, в какой последовательности они были отправлены.
10

по умолчанию, stdout является строковым буферизированным, stderr не имеет буферизации и файл полностью буферизирован.

10

stdout буферизуется, поэтому будет выводиться только после печати новой строки.

Чтобы получить немедленный вывод, выполните следующие действия:

  • Печать в stderr.
  • Сделать stdout небуферизованным.
  • 6
    Или fflush(stdout) .
  • 2
    msgstr "выводится только после печати новой строки." Не только это, но как минимум 4 других случая. буфер заполнен, запись в stderr (этот ответ упоминается позже), fflush(stdout) , fflush(NULL) .
8

Вы можете fprintf вместо stderr, который небуферизирован. Или вы можете сбросить stdout, когда захотите. Или вы можете установить stdout небуферизованным.

6

Используйте setbuf(stdout, NULL); для отключения буферизации.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню