Что касается печати символов в C
int main()
{
printf("Hello"); // doesn't display anything on the screen
printf("\n"); // hello is display on the screen
return 0;
}
Все символы (кандидаты на печать) буферизируются до получения новой строки? Правильный?
Q1 - Почему перед печатью на терминале происходит ожидание символа новой строки?
Q2 - Где находятся символы первого printf (т.е. "Hello"
) в буфере?
Q3 - Каков поток печати printf()->puts()->putchar()
-> где сейчас? Водитель? Есть ли у драйвера элемент управления для ожидания до \ n?
Q4 - Какова роль stdout, которая прикреплена к процессу?
Нужна более глубокая картина. Не стесняйтесь редактировать вопрос, если что-то не имеет смысла.
4 ответа
Я начну с некоторых определений, а затем продолжу отвечать на ваши вопросы.
Файл: это упорядоченная последовательность байтов. Это может быть файл на диске, поток байтов, сгенерированных программой (такой как конвейер), сокет TCP/IP, поток байтов, полученных или отправленных на периферийное устройство (такое как клавиатура или дисплей) и т. Д. Последние два являются интерактивными файлами. Файлы, как правило, являются основным средством связи программы со средой.
Поток: это представление потока данных из одного места в другое, например, с диска в память, из памяти в диск, из одной программы в другую и т. Д. Поток - это источник данных, в который данные можно помещать (записывать) или взяты данные из (читать). Таким образом, это интерфейс для записи данных или чтения данных из файла, который может быть любого типа, как указано выше. Прежде чем вы сможете выполнить какую-либо операцию с файлом, файл должен быть открыт. Открытие файла связывает его с потоком. Потоки представлены FILE
тип данных, определенный в stdio.h
заголовок. FILE
Объект (это структура) содержит всю информацию о внутреннем состоянии соединения с соответствующим файлом, включая такие вещи, как индикатор положения файла и информацию о буферизации. FILE
объекты распределяются и управляются изнутри библиотечными функциями ввода / вывода, и вы не должны пытаться создавать свои собственные объекты FILE
типа, библиотека делает это за нас. Программы должны иметь дело только с указателями на эти объекты (FILE *
) а не сами объекты.
Буфер: Буфер - это блок памяти, который принадлежит потоку и используется для временного хранения потоковых данных. Когда первая операция ввода-вывода происходит с файлом, malloc
вызывается и буфер получается. Символы, которые записываются в поток, обычно накапливаются в буфере (перед передачей в файл кусками) вместо того, чтобы появляться, как только они выводятся прикладной программой. Точно так же потоки извлекают входные данные из среды хоста в блоках, а не по символам. Это сделано для повышения эффективности, так как файловый и консольный ввод / вывод медленнее по сравнению с операциями с памятью.
C
библиотека предоставляет три предопределенных текстовых потока (FILE *
) открыт и доступен для использования при запуске программы. Это stdin
(стандартный поток ввода, который является нормальным источником ввода для программы), stdout
(стандартный поток вывода, который используется для нормального вывода из программы), и stderr
(стандартный поток ошибок, который используется для сообщений об ошибках и диагностики, выдаваемых программой). Будь эти потоки буферизованными или небуферизованными, определяется реализацией и не требуется стандартом.
GCC
обеспечивает три типа буферизации - небуферизованная, блочная буферизация и линейная буферизация. Небуферизованный означает, что символы появляются в целевом файле сразу после записи (для выходного потока) или входные данные читаются из файла на символьной основе вместо чтения в блоках (для входных потоков). Буферизованный блок означает, что символы сохраняются в буфере и записываются или читаются как блок. Строковая буферизация означает, что символы сохраняются только до тех пор, пока новая строка не будет записана или прочитана из буфера.
stdin
а также stdout
блокируются ли они в буфере тогда и только тогда, когда они могут быть определены как не относящиеся к интерактивному устройству, в противном случае они буферизуются в строке (это верно для любого потока). stderr
по умолчанию всегда небуферизован.
Стандартная библиотека предоставляет функции для изменения поведения потоков по умолчанию. Ты можешь использовать fflush
заставить данные из буфера выходного потока (fflush
не определено для входных потоков). Вы можете сделать поток небуферизованным, используя setbuf
функция.
Теперь давайте перейдем к вашим вопросам.
Неотмеченный вопрос: Да, потому что stdout
обычно относится к терминалу дисплея, если вы не используете перенаправление вывода с помощью >
оператор.
Q1: он ждет, потому что stdout
новая строка буферизируется, когда ссылается на терминал.
Q2: символы буферизуются, ну, в буфере, выделенном для stdout
поток.
Q3: поток печати: память -> stdout
буфер -> дисплей терминала. Существуют также буферы ядра, управляемые ОС, через которые проходят данные, прежде чем они появятся на терминале.
Q4: stdout
относится к стандартному выходному потоку, который обычно является терминалом.
Наконец, вот пример кода для экспериментов, прежде чем я закончу свой ответ.
#include <stdio.h>
#include <limits.h>
int main(void) {
// setbuf(stdout, NULL); // make stdout unbuffered
printf("Hello, World!"); // no newline
// printf("Hello, World!"); // with a newline
// only for demonstrating that stdout is line buffered
for(size_t i = 0; i < UINT_MAX; i++)
; // null statement
printf("\n"); // flush the buffer
return 0;
}
printf
не записывает непосредственно на экран, вместо этого он записывает в выходной поток, который по умолчанию буферизуется. Причиной этого является то, что экран может быть даже не прикреплен, и вывод также может быть отправлен в файл. По соображениям производительности, для системы будет лучше, если доступ к диску буферизуется и затем выполняется за один шаг с порциями соответствующего размера, а не каждый раз записывается.
Вы даже можете изменить размер буфера и установить его в 0, что означает, что весь вывод идет непосредственно к цели, что может быть полезно для целей регистрации.
setbuf(stdout, NULL);
Буфер сбрасывается либо при заполнении, либо при заполнении определенных критериев, например при печати новой строки. Поэтому, когда вы выполняете printf в цикле, вы заметите, что он будет записываться кусками, если между ними нет новой строки.
Если вы хотите, вы можете очистить символы перед новой строкой, вызвав
fflush(stdout);
Это может быть удобно, если вы медленно печатаете что-то вроде индикатора выполнения, где каждый символ печатается без новой строки.
int main()
{
printf("Hello"); // Doesn't display anything on the screen
fflush(stdout); // Now, hello appears on the screen
printf("\n"); // The new line gets printed
return 0;
}
Да, по умолчанию стандартный вывод буферизуется при подключении к терминалу. Буфер управляется операционной системой, обычно вам не о чем беспокоиться.
Вы можете изменить это поведение, используя setbuf()
или жеsetvbuf()
например, чтобы заменить его без буфера:
setbuf(stdout, NULL);
Все функции printf
, puts
, putchar
выводит на стандартный вывод, поэтому они используют один и тот же буфер.