K&R C Programming Language - Reverse Polish Calculator

150 Views Asked by At

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:

  1. Why the line s[1] = '\0'? There aren't any statements in the while loop checking for blanks, so wouldn't an input of 1 2 - 4 5 + * just result in \0 2 - 4 5 + *?
  2. 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 the i = 0, aren't we always starting at the beginning of the input string?
  3. For the - and / operators, why must we set op2 = pop()? I understand that these are not commutative operators, but shouldn't pop() - pop() or pop() / pop() be enough to describe the order of operands?
1

There are 1 best solutions below

0
Barmar On
  1. Notice that the while loop assigns s[0] each time through the loop. So s will just be a 1-character string. The null terminator is put in the second byte, which is s[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 uses s[i] = '\0'; after the loops to add the proper null terminator there. It might have been clearer to put s[1] = '\0' in the if (!isdigit(c) && c != '.') block, since that's the only case where we return before the later loops.

  2. getop() uses getch() to get characters from standard input. It's not processing the same string that was returned previously, it's overwriting it.

  3. 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 either 1 or -1.