В APUE (Расширенное программирование в среде UNIX) есть множество err_quit
, err_sys
и им подобных.
Источник, например, err_quit
следующий:
void err_quit(const char *fmt, ...) {
va_list ap;
va_start(ap, fmt);
err_doit(0, 0, fmt, ap);
va_end(ap);
exit(1);
}
static void err_doit(int errnoflag, int error, const char *fmt, va_list ap) {
char buf[MAXLINE];
vsnprintf(buf, MAXLINE-1, fmt, ap);
if (errnoflag)
snprintf(buf+strlen(buf), MAXLINE-strlen(buf)-1, ": %s",
strerror(error));
strcat(buf, "\n");
fflush(stdout); /* in case stdout and stderr are the same */ /* LINE A*/
fputs(buf, stderr);
fflush(NULL); /* flushes all stdio output streams */ /* LINE B*/
}
Мой вопрос LINE A
необходим из-за существования LINE B
? Не могу понять комментарии вLINE A
.
@DavidC.Rankin Я не вижу fputs (buf, "\n")
нигде в коде ОП, который на самом деле является кодом У. Рича Стивенса.
См. Почему printf не сбрасывается после вызова, если в строке формата нет новой строки? для более подробного обсуждения темы. @user207421 user207421, это было плохо fputs (buf, stderr);
-- старею...
Идея состоит в том, чтобы сбросить stdout
перед stderr
, чтобы их ожидающие выходные данные не перекрывались. Так что «более одного раза» на самом деле не применимо.
@DavidC.Rankin: stdout
не имеет «строчной буферизации по умолчанию». Это зависит от того, с чем оно связано. Итак, чтобы во всех случаях получить желаемое поведение, можно использовать fflush
, как указано выше для пользователя 207421.
Пока вы не перенаправляете его, он буферизуется по строкам.
@DavidC.Rankin: Re: «Пока вы не перенаправляете его, он буферизуется по строкам». Так? Это означает, что существуют ситуации, когда stdout
не буферизуется по строкам, поэтому для получения желаемого поведения необходимо использовать fflush
.
Извините, я не увидел в Вопросе ни одного подходящего случая. На какой текст вы там опираетесь?
@DavidC.Rankin Дело в том, что это очень защитный код, который не предполагает, что stderr
и stdout
различны или что stdout
не было перенаправлено.
@DavidC.Rankin: Какой угловой случай? Это простой основной случай. Если вы направляете и стандартный вывод, и стандартную ошибку в один и тот же канал или файл, они полностью буферизуются, и вывод может чередоваться. Когда программа записывает в stdout
, часть данных будет отправлена в канал или файл, когда буфер заполнен, а затем часть будет удерживаться в буфере. Затем, когда программа записывает в stderr
, это можно отправить в канал или файл, пока в буфере stdout
еще есть ожидающие данные, так что вы можете фрагменты сообщений из одного потока переплетать с данными из другого. Промывка позволяет избежать этого.
@tahzibi.jafar, Кроме того: вместо strlen(), strcat()
используйте возвращаемое значение vsnprintf()
(если не отрицательное), чтобы быстро определить длину буфера.
@tahzibi.jafar, Комментарий в fflush(NULL); /* flushes all stdio output streams */
неуместен. fflush(NULL);
очищает все выходные потоки, а не только все выходные потоки stdio. Сюда входят потоки вывода, связанные с файлами и т. д. Так что лучше как fflush(NULL); /* flush all output streams */
.
Эта программа, по-видимому, печатает как stdout
, так и stderr
. В Linux стандартный поток ошибок по умолчанию не буферизуется. Когда стандартный поток вывода подключен к каналу или файлу, он по умолчанию полностью буферизуется, и тогда вывод в эти потоки может стать смешанным. Небуферизованный означает, что программное обеспечение потоков отправляет выходные данные на устройство, как только программа их записывает. Полная буферизация означает, что программное обеспечение потока не отправляет выходные данные на устройство немедленно, а вместо этого записывает их внутри себя в буфер и отправляет на устройство только тогда, когда буфер заполнен или запрашивается очистка. Это сделано для повышения эффективности, поскольку уменьшает количество системных вызовов и операций ввода-вывода, необходимых для обработки выходных данных. Каждый буферизованный поток имеет отдельный буфер.
Например, предположим, что программа записала некоторый текст в stdout
до вызова этой подпрограммы err_doit
, возможно, текст Continuing program operations, everything is good.\n
, а затем вызывается err_doit
и записывает Error, the deaggregating fluxinator reported a zizzification error.\n
в обе stderr
. Что может произойти без вызовов fflush
:
stdout
, Continuing program operations,
заполнил буфер, поэтому программное обеспечение потока отправило полный буфер в канал или файл. everything is good.\n
в буфер.err_doit
позвонили и написали Error, the deaggregating fluxinator reported a zizzification error.\n
на stderr
. Поскольку стандартная ошибка не буферизована, она была отправлена немедленно.stdout
сбрасывается, everything is good.\n
отправляется в канал или файл.В результате канал или файл получает следующий текст:
Continuing program operations,Error, the deaggregating fluxinator reported a zizzification error.
everything is good.
Таким образом, пользователь в конечном итоге видит разрезанные и смешанные сообщения в канале или файле. Это не то, чего мы хотим. Чтобы избежать этого, программа сбрасывает stdout
перед записью в stderr
. Это гарантирует, что канал или файл получит:
Continuing program operations, everything is good.
Error, the deaggregating fluxinator reported a zizzification error.
Обычно вы этого не увидите, если запустите программу из командной строки и позволите стандартному выводу и стандартной ошибке подключиться к терминалу. Это связано с тем, что когда стандартный вывод подключен к терминалу, по умолчанию он буферизуется по строкам, то есть программа будет записывать вывод внутри себя и отправлять его на устройство только тогда, когда буфер заполнен, записывается символ новой строки, или требуется промывка. Таким образом, всякий раз, когда программа записывает строку, содержащую символ новой строки, вывод до этого символа немедленно отправляется на терминал. Это предотвращает переплетение данных stdout
и stderr
, пока программа заканчивает каждый вывод символом новой строки.
Это объясняет первое fflush(stdout);
. Причина последнего fflush(NULL);
(который запрашивает очистку всех потоков, возможно, ожидающих вывода) не ясна. Он очистит стандартный поток ошибок, если он буферизован, но это может быть намерением автора, а может и не быть.
Ваша единственная запись в
stderr
с помощьюfputs (buf, "\n");
(что само по себе является неправильным использованием ` int fputs(const char *s, FILE *stream);` )"\n"
не представляет собой объектFILE *stream
. Теперь сделайте шаг назад и спросите: «Почему я думаю, что мне нужноfflush(stdout);
?stdout
по умолчанию буферизуется по строкам, поэтому каждый раз, когда ваш вывод включает в себя символ'\n'
, эта строка будет записана вstdout
без необходимости вызоваfflush(stdout);
. Теперь, если вы простоfputs ("xxxx", stdout);
где нет'\n'
в конце"xxxx"
- тогда вам нужно будетfflush(stdout);
принудительно вывести"xxxx"
из буфера раньше.