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

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

9 ответов

Решение

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

Вместо этого выведите на stderr fprintf:

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

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

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

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

setbuf(stdout, NULL);

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

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

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

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

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

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

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

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

  1. Распечатать в stderr.
  2. Сделать стандартный вывод небуферизованным.

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

Обычно существует 2 уровня буферизации

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

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

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

Другие вопросы по тегам