Новый символ строки также очищает буфер?


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

Итак, я понимаю под буфером, который сбрасывается, то, что данные входные данные хранятся в буфере и передаются в stdout только тогда, когда они встречаются с endl или некоторыми явными функциями flush. Если да, то я ожидал, что следующий код:

#include <iostream>
#include <unistd.h>

int main(void)
{
    std::cout << "Hellonworld";
    sleep(2);
    std::cout << std::endl;

    return 0;
}

К дисплей:

через 2 секунды

Hello
World

Но фактический результат был:

Hello

через 2 секунды

World

Почему это так ?

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

2 4

2 ответа:

преобразование комментариев в ответ.

Это зависит от того, куда идет cout. Если он поступает на терминал ("интерактивное устройство"), то он не может быть полностью буферизован - обычно это буферизация строк, что означает, что символы появляются после печати новой строки или теоретически могут быть не буферизованы. Если он направляется в канал, файл или другое неинтерактивное место назначения, endl вытесняет данные, даже если поток полностью буферизован, как это обычно бывает.

Я также хотел знать, если я не предоставил ни нового символа строки, ни endl, будет ли вывод отображаться на stdout, Как только он достигнет конца программы, я знаю, что это делает для терминала, но применимо ли это ко всем типам stdout?

Да, когда поток файлов закрывается в (обычном) конце программы, ожидающий вывод будет сброшен. Он также будет сброшен, когда буфер будет заполнен. Если программа прерывается, ожидающий вывод обычно не будет сброшен.

Настройка по умолчанию для стандартных объектов потока C++ (std::cin, std::cout, std::cerr, и std::clog) заключается в том, что они синхронизируются с соответствующими потоками C(stdin, stdout, и stderr). Синхронизация означает, что чередующийся доступ потоков C++ и C приводит к согласованному поведению. Например, ожидается, что этот код создаст строку hello, world:

std::cout << "hel";
fprintf(stdout, "lo,");
std::cout << " wo";
fprintf(stdout, "rld");

Стандарт C++ не дает никаких указаний на то, как эта синхронизация реализуется. Один из способов его реализации-отключить любой буферизация для std::cout (и семейства) и немедленного доступа stdout. То есть, приведенный выше пример может сразу записать отдельные символы в stdout.

Если символы действительно записаны в stdout, будет использоваться настройка по умолчанию для режима буферизации для stdout. Я не могу найти спецификацию в стандарте, но обычно по умолчанию для режима буферизации stdout является _IOLBF, когда он подключен к интерактивному потоку (например, консоли), т. е. буфер сбрасывается в конце линии. По умолчанию для записи в файл обычно используется _IOFBF, то есть вывод сбрасывается, когда записывается полный буфер. В результате запись новой строки в std::cout может привести к тому, что буфер будет сброшен.

Потоки в C++ обычно настроены на буферизацию. То есть запись новой строки в файл, как правило, не приводит к немедленному появлению выходных данных (это произойдет только в том случае, если символ вызовет переполнение буфера в потоке, настроенном на отсутствие буфера). Поскольку синхронизация с stdout часто не нужна, например, когда программа всегда использует std::cout для записи в стандартный вывод, но вызывает довольно резкое замедление вывода в стандартный вывод (отключение буферизации для потока делает их медленными) синхронизация может быть отключена:

std::ios_base::sync_with_stdio(false);

Это отключает синхронизацию для всех объектов потока. Для плохой реализации не может быть никакого эффекта, в то время как хорошая реализация будет включать буферизацию для std::cout, приводящую к существенное ускорение и, вероятно, также отключение буферизации линий.

Как только поток C++ буферизуется, нет встроенного способа заставить его быть сброшенным при записи новой строки. Основная причина этого заключается в том, что работа с буферизацией строк потребует проверки каждого символа буфером потока, который эффективно блокирует массовые операции над символами и тем самым вызывает значительное замедление. При необходимости линейная буферизация может быть реализована с помощью простого фильтрационного потока буфер. Например:

class linebuf: public std::streambuf {
    std::streambuf* sbuf;
public:
    linebuf(std::streambuf* sbuf): sbuf(sbuf) {}
    int_type overflow(int_type c) {
        int rc = this->sbuf->sputc(c);
        this->sbuf->pubsync();
        return rc;
    }
    int sync() { return this->sbuf->pubsync(); }
};
// ...
int main() {
    std::ios_base::sync_with_stdio(false);
    linebuf sbuf(std::cout.rdbuf());
    std::streambuf* origcout = std::cout.rdbuf(&sbuf);

    std::cout << "line\nbuffered\n";

    std::cout.rdbuf(origcout); // needed for clean-up;
}

Tl;dr: стандарт C++ не имеет понятия буферизации строк, но он может получить его, когда стандартный ввод-вывод синхронизируется с поведением C stdout.