Действительно ли printf всегда очищает буфер при столкновении с новой линией?

Моя машина работает с ubuntu 10.10, и я использую стандартную библиотеку gnu C. У меня создалось впечатление, что printf сбросил буфер, если в строке формата была указана новая строка, однако следующий код неоднократно казался причиной этой тенденции. Может ли кто-нибудь уточнить, почему буфер не очищается.

#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/wait.h>

int main()
{
    int rc;
    close(1);
    close(2);
    printf("HI 1\n");
    fprintf(stderr, "ERROR\n");

    open("newfile.txt", O_WRONLY | O_CREAT | O_TRUNC, 0600);
    printf("WHAT?\n");
    fprintf(stderr, "I SAID ERROR\n");

    rc = fork();

    if (rc == 0)
    {
        printf("SAY AGAIN?\n");
        fprintf(stderr, "ERROR ERROR\n");
    }
    else
    {
        wait(NULL);
    }

    printf("BYE\n");
    fprintf(stderr, "HI 2\n");

    return 0;
}

Содержимое newfile.txt после запуска этой программы выглядит следующим образом.

HI 1
WHAT?
SAY AGAIN?
BYE
HI 1
WHAT?
BYE

Ответ 1

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

Это означает, что если вы перенаправляете stdout в файл, он не будет скрываться в новой строке. Если вы хотите попробовать принудительно привязать к строке, используйте setbuf или setvbuf.

В соответствующей части C99 7.19.3 Files, paragraph 7 указано:

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

Просто имейте в виду раздел 5.1.2.3/6:

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

Ответ 2

Он очищается, если устройство вывода является интерактивным, например, терминалом.

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

Подробнее см. paxdiablo answer.

Ответ 3

Вы пытались явно вызвать fflush(), чтобы увидеть, изменит ли он поведение?

Ответ 4

У тебя странное чувство юмора.:)

int main()
{
    int rc;
    close(1);
    close(2);
    printf("HI 1\n");
    fprintf(stderr, "ERROR\n");

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

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