Этот первый пример персонажа K&R поставил меня в тупик.
#include <stdio.h>
/* copy input to output; 2nd version */
main()
{
int c;
while ((c = getchar()) != EOF)
putchar(c);
}
Например, ввод abcd
напечатает abcd
. Я бы ожидал, что он напечатает только a
, потому что строка из стандартного ввода никогда не сохраняется полностью внутри этой программы, и даже если бы здесь ничего не было, она бы перебирала ее.
Я не понимаю, как getchar()
перебирает стандартный ввод, чтобы перейти к следующему символу. Это функция стандартного ввода или C?
Независимо от вашего вопроса, функция main()
без return
вызывает неопределенное поведение в C90 и не соответствует стандарту C. Это никогда не было правильным ни в одной версии C. Вывод: вам нужна книга получше.
В вашей системе запущено много ПО, которое не является частью кода C, который вы пишете. Просто подумайте обо всей ОС.... Некоторая часть этого программного обеспечения преобразует нажатия клавиш в соответствующие символы и помещает их в ваши программы (также известные как процесс) stdin. Это включает в себя буфер для хранения символов, которые ваша программа еще не прочитала. Как только вы вызываете getchar
, вам возвращается первый доступный символ (и удаляется из этого буфера). Другими словами, где-то в системе действительно есть буфер для стандартного ввода, но он не является частью вашего кода. Вам кто-то помог....
@malkaroee Спасибо! Я искал указатели чтения и нашел Документы IBM, который говорит, что getchar()
читает, а затем перемещает позицию потока, и документ окон, который рисовал достойную картину того, как осуществляется доступ к потоку. Я проверил эту функциональность итерации, написав getchar и putchar в последовательности N раз вне цикла, и, как и ожидалось, он напечатал строку до N символов. Дикий!
Цитата @GBE: «... и, как и ожидалось, он напечатал строку до N символов. Wild!» ну, это было бы еще более "дико", если бы этого не было ;-)
@GBE И для удовольствия вы даже можете добавить 5-секундный сон между каждым getchar
. Это по-прежнему будет работать... кто-то позаботится о буферизации символов для вас. Никакой магии.
getchar
является итерация
@Lundin функция main()
без return
не соответствует стандарту C. О чем ты говоришь? Функция main
без return
была совершенно законно во всех версиях C, начиная с C99.
@SteveSummit Если вы собираетесь спорить об этом, укажите правильные источники. ISO 9899:1990 6.6.6.4 «если выполняется оператор return без выражения и значение вызова функции используется вызывающей стороной, поведение не определено. Достижение }
, которое завершает функцию, эквивалентно выполнению оператора return без выражение."
@Lundin Существует явное исключение для функции с именем main
, как подробно цитируется в вопрос, с которым я связался.
@SteveSummit Да в C99 и выше. Но K&R не был написан для C99, и main()
не будет компилироваться в C99. Так что это всегда было неправильно, как ни крути.
@Lundin Тогда согласен не согласиться.
Вы вводите "abcd"
It reads 'a', checks if it is not equal to EOF.
If not prints 'a'
Then you go to the beginning of the loop:
It reads 'b', checks if it is not equal to EOF.
If not prints 'b'
Then you go to the beginning of the loop:
.....
It returns EOF, checks if it is not equal to EOF.
As it is equal it terminates the wile loop
getchar()
нет "читает" EOF. getchar
обнаруживает, что поток исчерпан, и возвращает EOF, чтобы указать, что вы достигли конца файла.
@WilliamPursell Это шалость. Стандарт C говорит: «Если поток находится в конце файла, устанавливается индикатор конца файла для потока, и getchar возвращает EOF».
@Lundin Это совсем не мудрствование. Существует мифология, что EOF — это символ, который на самом деле присутствует в потоках. «getchar возвращает EOF» на очень отличается от «getchar читает EOF», и я рад видеть, что ответ был отредактирован, чтобы удалить эту фразу.
@WilliamPursell Streams — это довольно мифологическая концепция для начала, это слой абстракции над простым входным буфером. В стандарте C не упоминается, как библиотечные функции могут узнать, пуст ли поток или нет. Очень вероятно, прочитав счетчик размера, но это не указано.
getchar
читает из поток. Вот что такое stdin
. Поток — это структура данных, которая представляет собой последовательность данных, которые считываются или куда-то записываются. Поток не похож на одну переменную. Это не похоже на массив. Это не то, что вы «перебираете». Это вам что-то получить от (или положить).
Вы можете думать об этом как о колоде карт, которую вы используете в игре. То есть вы можете проделывать такие операции, как: посмотреть карту сверху колоды, взять карту сверху колоды и положить в руку, взять N карт сверху колоды и положить их в свою рука.
Итак, когда вы видите код вроде
while ((c = getchar()) != EOF)
putchar(c);
используя аналогию с колодой карт, вы можете думать об этом как
while ( I try to take a card from the top of the deck and I do get one )
put the card in my hand;
Как правило, вы можете взять довольно много карт. Однако, в конце концов, вся колода будет израсходована, и ваш цикл остановится.
Звонок getchar
не похож на звонок sqrt(4)
. Если вы позвоните sqrt(4)
несколько раз, вы каждый раз будете получать один и тот же ответ. Но если вы позвоните getchar()
несколько раз, вы каждый раз будете получать ответ другой. (Формально мы говорим, что getchar
имеет штат, а его вызов имеет побочный эффект. В частности, каждый раз, когда вы вызываете getchar
, он имеет побочный эффект постоянного удаления одного символа из потока, то есть обновления состояния потока. чтобы зафиксировать тот факт, что был прочитан еще один символ.)
P.S. Аналогия с колодой карт не идеальна. С обычным потоком данных невозможно сделать что-то вроде взятия карты из середины колоды или перетасовки колоды. Но вы можете, как правило, положить одну карту обратно на верх колоды (это называется ungetc
в C). Кроме того, внутри, когда колода становится низкой, если вы читаете из файла, механизм stdio может взять кучу новых карт (еще один блок из файла) и вставить их в конец колоды.
Это хороший вопрос, вы можете думать о стандартном вводе как о временном файле. Вы пишете на него, а
getchar()
читает с него. Входной буфер где-то хранится, иgetchar()
считывает из него один символ и перемещает указатель чтения на единицу.