Can anyone explain to me the purpose of ungetch?
This is from K&R chapter 4 where you create a Reverse Polish Calculator.
I've ran the program without the call to ungetch and in my tests it still works the same.
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;
}
}
(I've removed the ternary operator in getch to make it clearer.)
I don't know about the specific example you're referring to (It's probaby 23 years since I read K&R, and that was the first edition.), but often when parsing it's convenient to 'peek' at the next character to see if it is part of what you're currently parsing. For instance, if you're reading a number you want to keep reading digits until you come to a non-digit. Ungetc lets the number reader look at the next character without consuming it so that someone else can read it. In Greg Hewgill's example of "2 3+", the number reader would read the 3 digit, then read the plus sign and know the number is finished, then ungetc the plus sign so that it can be read later.
Try running the program without spaces around operators. I don't recall precisely the format of that example and I don't have K&R handy, but instead of using "2 3 +" try "2 3+". The ungetch()
is probably used when parsing numbers, as the number parser will read digits until it gets something that is a non-digit. If the non-digit is a space, then the next getch()
will read the +
and all is well. However, if the next non-digit is a +
, then it will need to push that back onto the input stream so the main read loop can find it again.
Hope I'm remembering the example correctly.
It's used a lot for lexical scanners (the part of the compiler that breaks your text into chunks like variable names, constants, operators, etc.). The function isn't necessary for the scanner, it's just very convenient.
When you're reading a variable name, for example, you don't know when you're done until you read a character that can't be part of the variable name. But then you have to remember that character and find a way to communicate it to the next chunk of the lexer. You could create a global variable or something, or pass it to the caller--but then how do you return other things, like error codes? Instead, you ungetch() the character to put it back into the input stream, do whatever you need to with your variable name and return. Then when the lexer starts reading the next chunk, it doesn't have to look around for extra characters lying around.
Take a look at this code, you'll understand:
#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;
}