Какова цель ungetc (или ungetch от K & R)? - PullRequest
10 голосов
/ 24 января 2009

Может кто-нибудь объяснить мне цель ungetch? Это из K & R главы 4, где вы создаете калькулятор обратного польского.

Я запустил программу без вызова ungetch, и в моих тестах она все еще работает.

 int getch(void) /* get a (possibly pushed back) character */
    {
        if (bufp > 0)
        {
            return buf[--bufp];
        }
        else
        {
            return getchar();
        }
    }

    void ungetch(int c) /* push character back on input */
    {
        if (bufp >= BUFSIZE)
        {
            printf("ungetch: too many characters\n");
        }
        else
        {
            buf[bufp++] = c;
        }

}

(Я удалил троичный оператор в getch, чтобы сделать его более понятным.)

Ответы [ 4 ]

27 голосов
/ 24 января 2009

Я не знаю о конкретном примере, на который вы ссылаетесь (вероятно, прошло 23 года с тех пор, как я прочитал K & R, и это было первое издание.), Но часто при разборе удобно «заглядывать» на следующий символ чтобы увидеть, является ли это частью того, что вы сейчас анализируете. Например, если вы читаете номер, вы хотите продолжать читать цифры, пока не дойдете до цифры. Ungetc позволяет считывателю чисел смотреть на следующий символ, не потребляя его, чтобы кто-то другой мог его прочитать. В примере «2 3+» Грега Хьюгилла считыватель чисел прочитал бы 3-значную цифру, затем прочитал знак плюс и узнал, что число закончено, а затем снял знак плюс, чтобы его можно было прочитать позже.

10 голосов
/ 24 января 2009

Попробуйте запустить программу без пробелов вокруг операторов. Я не помню точно формат этого примера, и у меня нет K & R под рукой, но вместо использования «2 3+» попробуйте «2 3+». ungetch(), вероятно, используется при разборе чисел, так как анализатор чисел будет читать цифры, пока не получит что-то не-цифра. Если не цифра является пробелом, то следующий getch() будет читать +, и все хорошо. Однако, если следующая не цифра - +, то необходимо будет вернуть ее обратно во входной поток, чтобы основной цикл чтения мог найти его снова.

Надеюсь, я правильно помню пример.

4 голосов
/ 24 января 2009

Он часто используется для лексических сканеров (часть компилятора, которая разбивает ваш текст на куски, такие как имена переменных, константы, операторы и т. Д.). Функция не необходима для сканера, она просто очень удобна.

Когда вы, например, читаете имя переменной, вы не знаете, когда закончите, пока не прочитаете символ, который не может быть частью имени переменной. Но тогда вы должны запомнить этого персонажа и найти способ передать его следующему фрагменту лексера. Вы можете создать глобальную переменную или что-то в этом роде или передать ее вызывающей стороне, но тогда как вы возвращаете другие вещи, например коды ошибок? Вместо этого вы убираете () символ, чтобы поместить его обратно во входной поток, делаете все, что вам нужно, с именем переменной и возвращаете. Затем, когда лексер начинает читать следующий блок, ему не нужно искать лишние символы, лежащие вокруг.

0 голосов
/ 16 июля 2012

Посмотрите на этот код, вы поймете:

#include <conio.h>
#include <stdio.h>
int main()
{
    int y=0;
    char t[10];
    int u=0;
    ungetch('a');
    t[y++]=getch();
    ungetch('m');
    t[y++]=getch();
    ungetch('a');
    t[y++]=getch();
    ungetch('z');
    t[y++]=getch();
    ungetch('z');
    t[y++]=getch();
    ungetch('a');
    t[y++]=getch();
    ungetch('l');
    t[y++]=getch();
    ungetch('\0');
    t[y++]=getch();
    ungetch('\0');
    t[y++]=getch();
    ungetch('\0');
    t[y++]=getch();
    printf("%s",t);
    return 0;
}
...