Книга K&R 1.5.1 Копирование файлов

Я просмотрел сайт в отношении этого примера K&R, и ответы, кажется, вращаются вокруг «почему это тип int или что такое EOF?» вроде ребята. Я верю, что понимаю тех. Это результаты, которые я не понимаю. Я ожидал, что этот код будет принимать один символ, печатать его, а затем ждать другого символа или EOF.

Результаты, которые я вижу, - это ввод, ожидающий, пока я не нажму «Ввод», затем все, что я набрал, появляется и больше ждет ввода.

Является ли цикл while просто «зацикленным», пока я не закончу текстовый поток с возвратом каретки, а затем не покажу, что где-то скрывал putchar (c)?

Код:

#include <stdio.h>

/* copy input to output: 1st version */
main()
{
    int c;

    c = getchar();
    while(c != EOF) {
        putchar(c);
        c = getchar();
    }  
}

Теперь, если я прокрадываю putchar(c) до того, как на линии как раз перед тем, как я получу то, что ожидал. Я все еще должен ввести текстовый поток и нажать клавишу возврата. Результатом является первый символ потока, и программа завершает работу.

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

Спасибо за помощь


person Old Sub Sailor    schedule 18.04.2016    source источник
comment
Не используйте стиль кодирования K&R, он устарел. C развивался последние ›17 лет. О вашем тексте: не знаю, какой у вас конкретный вопрос. Это не обучающий сайт.   -  person too honest for this site    schedule 18.04.2016
comment
Это связано с тем, что стандартный ввод и вывод буферизуются: введенные вами символы сохраняются внутри до тех пор, пока вы не нажмете Enter, а затем обрабатываются в пакетном режиме. Точно так же выходные символы сохраняются до тех пор, пока вы не напечатаете символ новой строки, после чего все сбрасывается. Если вы хотите прямого соответствия между вводом и выводом, вы должны использовать специальные библиотеки вместо консольного ввода-вывода.   -  person M Oehm    schedule 18.04.2016
comment
И CR (он же возврат каретки) не завершает поток.   -  person too honest for this site    schedule 18.04.2016
comment
Хрустальный шар говорит, что вы не знаете, как генерировать EOF с помощью клавиатуры. Вам не нужно отключать клавиатуру. Если вы используете Linux или OSX, нажмите Ctrl+D. Если вы используете Windows, нажмите Ctrl+Z. Попробуйте перенаправить ввод, чтобы сделать его более понятным/полезным.   -  person Hans Passant    schedule 18.04.2016
comment
@Olaf Книга по-прежнему очень актуальна. И я думаю, что это правильный вопрос, хотя он объяснен в книге.   -  person Leandros    schedule 18.04.2016
comment
@MOehm: stdout должен сбрасываться, когда вы читаете из stdin.   -  person EOF    schedule 18.04.2016
comment
@Leandros: я не буду обсуждать дидактику. Но он учит древнему, частично нестандартному и устаревшему стилю программирования C. Также вопрос плохо изучен и неясно, в чем заключается реальная проблема ОП.   -  person too honest for this site    schedule 18.04.2016
comment
@EOF: Хорошо, но чтение происходит только после нажатия кнопки возврата. В результате вы получаете чередующиеся строки ввода и вывода.   -  person M Oehm    schedule 18.04.2016
comment
@Olaf Первая версия учит старому, нестандартному C, это правильно. Этого следует избегать. Вторая версия обучает ANSI-C, который все еще актуален. Большое количество C все еще написано в ANSI-C.   -  person Leandros    schedule 18.04.2016
comment
Страдание от буферизации может помочь.   -  person Schwern    schedule 18.04.2016
comment
@Leandros: Если бы я имел в виду первое издание, я бы написал «27 лет». C90 также древний и имеет некоторые проблемы совместимости с C99 (хотелось бы, чтобы их было больше, они должны были выбросить гораздо больше старого русского).   -  person too honest for this site    schedule 18.04.2016
comment
@ Олаф Ну да, он старый. Но не устарел. Тонна старого и нового кода C написана на C90, вероятно, в первую очередь ядро ​​​​Linux. Его также хорошо поддерживает Microsoft, отказывающаяся поддерживать что-либо более позднее, чем C90. Но мы не должны обсуждать это здесь, я согласен. Прости. Но мне все еще любопытно, почему у C90 должны быть проблемы совместимости с C99? Разве каждый код C90 не является также действительным C99?   -  person Leandros    schedule 18.04.2016
comment
Я могу думать только об одном, с использованием указателей restrict. Нарушение строгого алиасинга в C90, но не в C99 с помощью ключевого слова ограничения. Но это не строго одна и та же программа.   -  person Leandros    schedule 18.04.2016
comment
@Leandros, утверждение о том, что ядро ​​​​Linux написано на C90, просто неверно. Linux является пост-C99, но даже не совместим с C99, он требует компиляции расширений GNU.   -  person Antti Haapala    schedule 01.04.2019


Ответы (2)


По умолчанию стандартный ввод и стандартный вывод буферизуются. Это означает, что они накапливают партии символов и отправляют их сразу для эффективности. Как правило, пакет сохраняется до тех пор, пока в буфере не останется места или пока в потоке не появится новая строка или EOF.

Когда вы вызываете getchar(), вы запрашиваете символы из стандартного ввода. Предположим, вы набираете A, этот символ сохраняется в буфере, а затем система ожидает дальнейшего ввода. Если вы наберете B, этот символ будет помещен в буфер следующим. Возможно, после этого вы нажмете Enter, и в буфер будет помещена новая строка. Но новая строка также прерывает процесс буферизации, поэтому первоначальный вызов getchar() возвращает первый символ в буфере (A). На следующей итерации вы снова вызываете getchar(), и он немедленно возвращает следующий символ в буфере (B). И так далее.

Таким образом, дело не в том, что ваш цикл while выполняется до тех пор, пока вы не закончите строку, а в том, что первый вызов getchar() (когда буфер пуст) ожидает, пока он не заполнит буфер или не увидит новую строку.

Когда вы чередуете функции вывода, такие как putchar(), большинство библиотек времени выполнения C будут «сбрасывать» стандартный ввод, когда вы делаете что-то, что отправляет данные в стандартный вывод (и наоборот). (Намерение состоит в том, чтобы убедиться, что пользователь увидит приглашение до того, как программа будет ожидать ввода.) Вот почему вы начали наблюдать другое поведение, когда добавили вызовы putchar().

Вы можете вручную очистить буфер, используя функцию flush(). Вы также можете контролировать размер буфера, используемого стандартными потоками, используя setvbuf().

Как отметил Хан Пассант в комментариях, новая строка не «завершает поток». Чтобы получить EOF на stdin, вы должны нажать Ctrl+D (или, в некоторых системах, Ctrl+Z). EOF также очищает буфер. Если вы перенаправили файл или вывод из другой программы на стандартный ввод, EOF произойдет, как только этот ввод будет исчерпан.

Хотя это правда, что K&R C очень стар, и даже ANSI C сегодня не так распространен, как раньше, все, что касается буферизации с помощью stdin и stdout, фактически одинаково в текущих стандартах и ​​даже в C++. Я думаю, что единственное существенное изменение заключается в том, что стандарты C теперь явно указывают на желательность того, чтобы stdin и stdout приводили к сбросу другого.

person Adrian McCarthy    schedule 18.04.2016

Я ценю ваш ответ, и описанная вами буферизация очень полезна и интересна.

Очевидно, я также, должно быть, неправильно прочитал/понял K&R. Они определяют текстовый поток как «... состоит из нуля или более символов, за которыми следует символ новой строки», что я понял для обозначения клавиши возврата/ввода; заканчивая его, а затем разрешая вывод.

Кроме того, я хотел бы поблагодарить всех вас, кто оставил полезные комментарии.

Кстати, я четко понял, что мне нужно ввести ^D, чтобы сгенерировать EOF, который завершает программу. Я ценю, что вы все программисты высшего уровня, и благодарю вас за ваше время. Я предполагаю, что мне нужно будет найти другое место, чтобы обсудить, о чем идет речь в тексте, написанном R&R относительно этого упражнения.

person Old Sub Sailor    schedule 21.04.2016