Почему printf не сбрасывается после вызова, если новая строка не находится в строке формата? - PullRequest
485 голосов
/ 11 ноября 2009

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

Ответы [ 9 ]

636 голосов
/ 11 ноября 2009

Поток 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

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

setbuf(stdout, NULL);
120 голосов
/ 17 ноября 2010

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

27 голосов
/ 11 ноября 2009

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

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

24 голосов
/ 11 ноября 2009

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

15 голосов
/ 27 октября 2010

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

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

11 голосов
/ 29 июля 2010

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

11 голосов
/ 11 ноября 2009

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

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

  1. Печать в stderr.
  2. Сделать стандартный вывод небуферизованным.
10 голосов
/ 11 ноября 2009

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

8 голосов
/ 31 мая 2015

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

...