В примере с самой длинной строкой K&R 1.9, что делает getchar()?

Кажется, теперь я понимаю программу, за исключением того, что функция getline не очень интуитивно понятна, поскольку кажется, что она копирует все, что getchar() возвращает, в массив символов s[], который никогда не используется для чего-то важного.

int getline(char s[], int lim)
{
    int c, i;
    for(i=0; i < lim - 1 && (c = getchar()) != EOF && c != '\n'; ++i)
        s[i] = c;
    if(c == '\n')
    {
        s[i] = c;
        ++i;
    }
    s[i] = '\0';
    return i;
}

С таким же успехом функция может игнорировать строку s[i] = c;, потому что все, что на самом деле делает функция, — это подсчет количества символов, пока не достигнет EOF или '\n' не вернется из getchar().

Чего я действительно не понимаю, так это почему программа продвигалась вперед, поскольку основной цикл выглядит следующим образом:

main()
{
    int len; /* current line length */
    int max; /* maximum length seen so far */
    char line[MAXLINE]; /* current input line */
    char longest[MAXLINE]; /* longest line saved here */

    max = 0;
    while ((len = getline(line, MAXLINE)) > 0)
        if (len > max)
        {
            max = len;
            copy(longest, line);
        }
    if (max > 0) /* there was a line */
        printf("%s", longest);
    return 0;
}

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

Так программа продвигается? Входит ли программа сначала в цикл while, а затем ждет, пока пользователь введет строку текста, и как только пользователь нажимает клавишу ввода, цикл for функции getline повторяется? Я чувствую, что это так, поскольку пользователь может ввести backspace во время ввода.

Мой вопрос в том, как именно программа вообще продвигается вперед? Это все из-за функции getchar()?

Когда я нажимаю ctrl-D в терминале, происходят некоторые другие запутанные вещи. Если я нажму ctrl-D в начале новой строки, программа завершится. Если я нажму ctrl-D в конце строки, заполненной каким-либо текстом, она не завершится и не будет действовать так же, как нажатие enter< /кбд>. Если я несколько раз нажму ctrl-D в строке с текстом, программа, наконец, завершится.

Это просто то, как мой терминал обрабатывает сеанс, или это все, о чем мне не следует беспокоиться, если я просто хочу выучить C?

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


person Leonardo    schedule 28.03.2013    source источник


Ответы (4)


В объявлении параметра (и только в этом контексте) char s[] на самом деле означает char *s. Стандарт C описывает это следующим образом:

Объявление параметра как "массив типа" должно быть изменено на "полный указатель на тип".

Так что s на самом деле является указателем типа char*, и когда функция изменяет s[i], она изменяет i элемент line.

По вызову:

getline(line, MAXLINE)

line — это массив, но в большинстве контекстов выражение массива неявно преобразуется в указатель на первый элемент массива.

Эти два правила кажутся частью заговора, направленного на то, чтобы заставить выглядеть массивы и указатели на самом деле одним и тем же в C. Это определенно не так. Объект-указатель содержит адрес некоторого объекта (или нулевой указатель, который не указывает ни на какой объект); объект массива содержит упорядоченную последовательность элементов. Но большая часть манипуляций с массивами в C выполняется с помощью указателей на элементы массива, при этом арифметика указателей используется для перехода от одного элемента к другому.

Предлагаемая литература (я часто об этом говорю): раздел 6 часто задаваемых вопросов о comp.lang.c.

person Keith Thompson    schedule 29.03.2013
comment
+1 за правильный ответ, в основном то, что я сказал, чтобы решить проблему ОП, но с гораздо лучшей документацией. - person Peter Wooster; 29.03.2013

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

Вы ошибаетесь насчет функции. Массив передается функции*, и она сохраняет каждый символ, прочитанный getchar (кроме EOF или новой строки) в последовательных элементах. В том то и смысл - не считать символы, а хранить их в массиве.

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

person teppic    schedule 28.03.2013
comment
Спасибо, теперь я это понимаю. Но теперь я смущен тем, как программа заканчивается. Почему нажатие Ctrl-D не работает так же, как нажатие Enter? - person Leonardo; 29.03.2013
comment
Это зависит от того, что ваша система делает с Ctrl-D. Иногда вам нужно нажать ее дважды, чтобы отправить EOF. - person teppic; 29.03.2013
comment
Не совсем корректно передавать массив в функцию. На самом деле передается (по значению, поскольку передаются все аргументы C) указатель на первый элемент массива. - person Keith Thompson; 29.03.2013
comment
@KeithThompson - я просто старался максимально упростить объяснение. Поскольку функция рассматривает его как массив, я не вижу необходимости вводить указатели. - person teppic; 29.03.2013
comment
@teppic: На какое-то время вам может сойти с рук такое предположение, но в конце концов вы задаетесь вопросом, насколько велик s[] и почему sizeof s дает вам размер указателя. (Я не утверждаю, что вы этого не понимаете.) Строго говоря, функция обращается с s как с указателем; оператор [] определяется с точки зрения арифметики указателей. - person Keith Thompson; 29.03.2013
comment
@KeithThompson - я согласен с этим. Я немного уточнил ответ. Под лечением я подразумеваю только с точки зрения синтаксиса. - person teppic; 29.03.2013
comment
Это зависит от того, что ваша система делает с Ctrl-D. Иногда вам нужно нажать ее дважды, чтобы отправить EOF. -- Это вполне определено. ^D на терминале завершает read без добавления чего-либо в буфер терминала (в отличие от ввода, который завершает вызов чтения после добавления \n в буфер). Таким образом, если вы наберете ^D в качестве первой клавиши, нажатой на терминале, или сразу после ввода ввода, или сразу после другого ^D, read вернет 0, потому что буфер терминала пуст. Почти все программное обеспечение POSIX интерпретирует это как EOF. Некоторое программное обеспечение может сбить с толку строками, которые не заканчиваются на \n. - person Jim Balter; 02.04.2013
comment
@JimBalter - опять же, это было просто упрощение. Хотя я, вероятно, должен был сказать: «Один раз в непустой строке не будет генерироваться EOF, но дважды будет». - person teppic; 02.04.2013
comment
опять же, это было просто упрощение... и я тщательно изложил это. Поскольку все, что я написал, было неверным, это не требовало ответа. - person Jim Balter; 06.04.2013

Массив используется для чего-то важного: он предоставляется вызывающей стороной и возвращается измененным с новым содержимым. С разумной точки зрения заполнение массива и есть цель вызова функции.

person wallyk    schedule 28.03.2013

Этот массив (ссылка на массив) на самом деле является указателем, char s[] совпадает с char *s. поэтому он строит свой результат в этом массиве, поэтому он позже копируется в main. в K&R редко бывает какая-то «магия».

person Peter Wooster    schedule 28.03.2013
comment
-1: массивы не являются указателями, указатели не являются массивами. Я предлагаю вам прочитать раздел 6 часто задаваемых вопросов о comp.lang.c. - person pmg; 29.03.2013
comment
Хорошо, теперь я понимаю, что на самом деле делает функция getline. Но тогда функция getchar() также заставляет программу продвигаться по вводу, как я подозревал? Я был просто немного смущен тем, как программа узнает, что нужно начать читать новые вещи. Но было бы разумно, чтобы функция getchar() постепенно продвигала программу вперед с любым новым вводом. Однако я немного смущен тем, как программа завершается. - person Leonardo; 29.03.2013
comment
@pmg прочитал A7.3.1 из K&R, ссылка на массив, массивы не являются указателями, а ссылки на них, и этот массив является ссылкой на массив. - person Peter Wooster; 29.03.2013
comment
Я отозвал -1, но массивы не являются указателями — во многих случаях они преобразуются в указатели на свой первый элемент. - person pmg; 29.03.2013
comment
Массивы не являются указателями, но в контексте объявления параметра (и только в этом контексте) char s[] на самом деле означает char *s. - person Keith Thompson; 29.03.2013
comment
Спасибо, ссылки на массивы, которыми является этот, поскольку он является параметром функции, являются указателями, по крайней мере, в K&R. - person Peter Wooster; 29.03.2013
comment
@PeterWooster: неясно, что вы подразумеваете под ссылками на массивы. Есть два важных правила: преобразование параметра массива в параметр указателя во время компиляции и неявное преобразование (в большинстве случаев) выражения массива в указатель на первый элемент массива. - person Keith Thompson; 29.03.2013
comment
@KeithThompson, этот термин используется, но не определен в K&R в разделе A7 о выражениях. Я понимаю, что это означает ссылку на массив, а не на сам массив. - person Peter Wooster; 29.03.2013
comment
@PeterWooster: у меня нет под рукой K&R. Это 1-я или вторая версия? (В стандарте ISO используется фраза выражение, имеющее тип «массив type», что кажется мне более точным.) - person Keith Thompson; 29.03.2013
comment
@KeithThompson эта фраза также используется. Термин ссылка на массив используется только в A7.3.1 при обсуждении постфиксных выражений. Версия K&R - это второе издание 1988 года. - person Peter Wooster; 29.03.2013