I'm relatively new to C (but decently well versed in programming in general) and I've been trying to self study via the K&R C book. I've found myself stumped when trying to understand how the example Reverse Polish Calculator works. Below I have provided the code:
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#define MAXOP 100
#define NUMBER '0'
#define MAXVAL 100
#define BUFSIZE 100
int getop(char []);
void push(double);
double pop(void);
int getch(void);
void ungetch(int);
int main(void) {
int type;
double op2;
char s[MAXOP];
while ((type = getop(s)) != EOF) {
switch (type) {
case NUMBER:
push(atof(s));
break;
case '+':
push(pop() + pop());
break;
case '*':
push(pop() * pop());
break;
case '-':
op2 = pop();
push(pop() - op2);
break;
case '/':
op2 = pop();
if (op2 != 0.0)
push(pop() / op2);
else
printf("error: zero divisor\n");
break;
case '\n':
printf("\t%.8g\n", pop());
break;
default:
printf("error: unknown command %s\n", s);
break;
}
}
return 0;
}
int sp = 0;
double val[MAXVAL];
void push(double f) {
if (sp < MAXVAL)
val[sp++] = f;
else
printf("error: stack full, can't push %g\n", f);
}
double pop(void) {
if (sp > 0)
return val[--sp];
else {
printf("error: stack empty\n");
return 0.0;
}
}
int getop(char s[]) {
int i, c;
while ((s[0] = c = getch()) == ' ' || c == '\t')
;
s[1] = '\0';
if (!isdigit(c) && c != '.')
return c;
i = 0;
if (isdigit(c))
while (isdigit(s[++i] = c = getch()))
;
if (c == '.')
while (isdigit(s[++i] = c = getch()))
;
s[i] = '\0';
if (c != EOF)
ungetch(c);
return NUMBER;
}
char buf[BUFSIZE];
int bufp = 0;
int getch(void) {
return (bufp > 0) ? buf[--bufp] : getchar();
}
void ungetch(int c) {
if (bufp >= BUFSIZE)
printf("ungetch: too many characters\n");
else
buf[bufp++] = c;
}
With this I have several questions:
- Why the line
s[1] = '\0'?There aren't any statements in the while loop checking for blanks, so wouldn't an input of1 2 - 4 5 + *just result in\0 2 - 4 5 + *? - Every time we check
type = getop(s), how does the program know to get the next operator or operand? If I remember correctly, having a null character in the middle of a character means every character after the null character is ignored. With thei = 0, aren't we always starting at the beginning of the input string? - For the - and / operators, why must we set
op2 = pop()? I understand that these are not commutative operators, but shouldn'tpop() - pop()orpop() / pop()be enough to describe the order of operands?
Notice that the
whileloop assignss[0]each time through the loop. Soswill just be a 1-character string. The null terminator is put in the second byte, which iss[1]. The loop is creating a string containing the next non-whitespace character. The code after that will extend the string for longer tokens, and usess[i] = '\0';after the loops to add the proper null terminator there. It might have been clearer to puts[1] = '\0'in theif (!isdigit(c) && c != '.')block, since that's the only case where we return before the later loops.getop()usesgetch()to get characters from standard input. It's not processing the same string that was returned previously, it's overwriting it.C doesn't specify the order of function calls in most expression. So if you write
pop() - pop(), there's no way of telling whether the top of the stack will be the first or second operand. For commutative operators this doesn't matter, but for subtraction and division you need to do the pops in separate statements to ensure that you get them in the proper order. Otherwise,3 2 -could result in either1or-1.