Почему стандартный вывод буферизуется?

Я пытаюсь изучить API libuv и написал следующий тест:

#include <stdio.h>
#include <stdlib.h>
#include <uv.h>

void timer_cb(uv_timer_t* timer) {
    int* i = timer->data;
    --*i;
    if(*i == 0) {
       uv_timer_stop(timer);
    }
    printf("timer %d\n", *i);
    //fflush(stdout);
}

int main() {
    uv_loop_t* loop = uv_default_loop();
    uv_timer_t* timer = malloc(sizeof(uv_timer_t));
    uv_timer_init(loop, timer);
    int i = 5;
    timer->data = &i;
    uv_timer_start(timer, timer_cb, 1000, 2000);

    uv_run(loop, UV_RUN_DEFAULT);

    printf("Now quitting.\n");
    uv_close(timer, 0);
    uv_loop_close(loop);

    return 0;
}

При ее запуске выходные данные не отображаются до тех пор, пока программа не завершит работу, а затем все выходные данные отображаются сразу. Если я раскомментирую строку fflush, она будет работать, как и ожидалось, записывая каждые 2 секунды.

Может кто-нибудь объяснить мне это? Почему stdout не сбрасывается после новой строки, как объясняется здесь и в других местах? Зачем мне нужно вручную сбрасывать его?


person Baruch    schedule 23.06.2016    source источник
comment
Почему буферизация stdout — почему бы и нет?   -  person too honest for this site    schedule 23.06.2016
comment
Этот ответ может быть вам полезен: stackoverflow.com/a/5229135/868691   -  person Nicholas Flees    schedule 23.06.2016
comment
@AndrewHenle Windows с mingw64 на msys   -  person Baruch    schedule 23.06.2016
comment
@Olaf Я понял (см. ответ, на который я ссылаюсь в конце), что буфер сбрасывается на новую строку   -  person Baruch    schedule 23.06.2016
comment
@baruch: Где вы нашли это в стандарте?   -  person too honest for this site    schedule 23.06.2016
comment
@LPs Я пробовал это. Не помогло   -  person Baruch    schedule 23.06.2016
comment
@LPs Как я уже сказал в комментариях к ответам, я уже пробовал это   -  person Baruch    schedule 23.06.2016
comment
Вы используете Эклипс? Посетите этот форум.   -  person LPs    schedule 23.06.2016
comment
stdout обычно буферизуется строкой, если вывод идет на терминал. Если он идет в файл, он обычно не буферизуется строкой.   -  person Tom Karzes    schedule 23.06.2016


Ответы (3)


Буферизация потока определяется реализацией.

Согласно 7.21.3 Files, параграф 3 Стандарт C:

Когда поток небуферизован, предполагается, что символы появляются из источника или в месте назначения как можно быстрее. В противном случае символы могут накапливаться и передаваться в хост-среду или из нее в виде блока. Когда поток полностью буферизован, символы предназначены для передачи в хост-среду или из нее в виде блока при заполнении буфера. Когда поток буферизируется строкой, символы предназначены для передачи в хост-среду или из нее в виде блока, когда встречается символ новой строки. Кроме того, символы предназначены для передачи в качестве блока в хост-среду при заполнении буфера, когда ввод запрашивается в небуферизованном потоке или когда ввод запрашивается в линейном буферизованном потоке, который требует передачи символов из хост-среды. . Поддержка этих характеристик определяется реализацией и может быть изменена с помощью функций setbuf и setvbuf.

Тип буферизации зависит от вашей реализации, и ваша реализация, по-видимому, не буферизует строки в вашем примере.

person Andrew Henle    schedule 23.06.2016
comment
Я установил буферизацию строк с помощью setvbuf(stdout, NULL, _IOLBF, 0);, и это все равно не работает. Только если я отключу буферизацию (_IONBF), это сработает - person Baruch; 23.06.2016
comment
Как указано в этом ответе, в Windows нет буферизации строк. Я предполагаю, что проблема заключается в msdn.microsoft.com/en-us/ библиотека/86cebhfs.aspx#Anchor_3 - person Baruch; 23.06.2016

Нет строгого требования, что stdout буферизуется строкой. Он также может быть полностью буферизован (или вообще не буферизован), и в этом случае \n не срабатывает для сброса потока.

C11 (N1570) 7.21.3/7 Файлы:

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

C11 (N1570) 5.1.2.3/7 Выполнение программы:

То, что представляет собой интерактивное устройство, определяется реализацией.

Вы можете попробовать принудительно задать определенный тип буферизации с помощью стандартной функции setvbuf. Например, чтобы установить буферизацию строк для stdout, вы можете попробовать:

setvbuf(stdout, buff, _IOLBF, size);

где buff объявлен как массив символов из size элементов (например, 1024).

Обратите внимание, что setvbuf необходимо вызывать перед любой другой операцией ввода-вывода, которая выполняется с потоком.

person Grzegorz Szpetkowski    schedule 23.06.2016
comment
Я установил буферизацию строк с помощью setvbuf(stdout, NULL, _IOLBF, 0);, и это все равно не работает. Только если я отключу буферизацию (_IONBF), это сработает - person Baruch; 23.06.2016
comment
@baruch: AFAIR, setvbuf требует предоставить ему самостоятельно подготовленный буфер. В противном случае это не сработает. - person Grzegorz Szpetkowski; 23.06.2016

По какой-то причине ваша система решает, что ваш стандартный вывод не является интерактивным. Вы делаете какое-то странное перенаправление stdout или делаете что-то странное со своим терминалом? Вы должны иметь возможность переопределить с помощью setbuf или вы можете использовать stderr вместо stdout.

person GroovyDotCom    schedule 23.06.2016
comment
Это не дает ответа на вопрос. Чтобы подвергнуть критике или запросить разъяснения у автора, оставьте комментарий под его публикацией. – Из обзора - person StepUp; 23.06.2016
comment
Я думаю, что он включает в себя 2 ответа: используйте setvbuf или stderr. Третий ответ только подразумевается, чтобы выяснить, почему система ошибочно принимает стандартный вывод за неинтерактивный. - person GroovyDotCom; 28.06.2016