Зачем мне использовать fflush на stdout перед написанием в stderr?

Я читаю "Сетевое программирование UNIX: сетевой API сокетов", и в примере кода у них есть функция обработки ошибок, которая содержит следующие строки:

fflush(stdout);     /* in case stdout and stderr are the same */
fputs(buf, stderr);
fflush(stderr);

где buf содержит описание ошибки. Я не понимаю, почему fflush используется в stdout в первой строке и почему комментарий объясняет причину его использования.

2 ответов


Это из-за буферизации. Stdout и stderr обычно буферизуются по-разному. Stdout это обычно строка буферизована, что означает, что она не будет отображать вывод, пока не увидит новую строку. Поток stderr составляет обычно unbuffered и будет печатать немедленно, думая, что вы должны увидеть сообщения об ошибках pronto.

но они оба идут в одно и то же место, терминал. Вот что значит /* in case stdout and stderr are the same */. Обычно так и есть. Но потому что они буферизованы иначе это может привести к тому, что они будут отображаться не в порядке.

рассмотрим этот код. Заметьте отсутствие строк.

#include <stdio.h>

int main() {
    fprintf(stdout, "This is to stdout. ");
    fprintf(stderr, "This is to stderr. ");
    fprintf(stdout, "This is also to stdout. ");
}

вы ожидаете, что выход будет:

This is to stdout. This is to stderr. This is also to stdout.

но это не так. Он не работает.

$ ./test
This is to stderr. This is to stdout. This is also to stdout.

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

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

#include <stdio.h>
#include <unistd.h>

int main() {
    fprintf(stdout, "This is to stdout. ");
    fflush(stdout);
    fprintf(stderr, "This is to stderr. ");
    fprintf(stdout, "This is also to stdout. ");
}

$ ./test
This is to stdout. This is to stderr. This is also to stdout. 

Это гарантирует, что сообщения об ошибках выходят в правильном порядке наряду с обычными сообщениями. Это позволяет избежать путаницы о том, какое сообщение об ошибке применяется к какой части программы.


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