K&R C Programs Exercise 5-10

Exercise 5-10. Write the program expr, which evaluates a reverse Polish expression from the command line, where each operator or operand is a separate argument. For example, expr 2 3 4 + * evaluates 2 * (3+4).

The K&R RPN calculator from Chapter 4 reads from stdin. This version reads from argv instead — each argument is either a number (atof) or a single-character operator. No getop/getch/ungetch machinery is needed; argc and argv supply the tokens directly.

Solution

/* K&R Exercise 5-10 — expr: RPN evaluator from command line
 * Compile: gcc -ansi -Wall ex5-10.c -o expr
 * Usage:   ./expr 2 3 4 + *    → 14 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define MAXVAL 100

static double val[MAXVAL];
static int sp = 0;

void push(double f)
{
    if (sp < MAXVAL) val[sp++] = f;
    else { fprintf(stderr, "expr: stack overflow\n"); exit(1); }
}

double pop(void)
{
    if (sp > 0) return val[--sp];
    fprintf(stderr, "expr: stack underflow\n"); exit(1);
}

int main(int argc, char *argv[])
{
    double op2;

    if (argc < 2) {
        fprintf(stderr, "usage: expr operand operator ...\n");
        return 1;
    }

    while (--argc > 0) {
        argv++;
        if ((*argv)[1] == '\0') {   /* possible single-char operator */
            switch (**argv) {
            case '+': push(pop() + pop()); continue;
            case '*': push(pop() * pop()); continue;
            case '-': op2 = pop(); push(pop() - op2); continue;
            case '/': op2 = pop(); if (op2 == 0.0) {
                          fprintf(stderr, "expr: division by zero\n"); return 1;
                      }
                      push(pop() / op2); continue;
            case '%': op2 = pop();
                      if (op2 == 0.0) {
                          fprintf(stderr, "expr: modulo by zero\n"); return 1;
                      }
                      push((long)pop() % (long)op2); continue;
            }
        }
        push(atof(*argv));   /* treat as a number */
    }
    printf("%g\n", pop());
    if (sp != 0)
        fprintf(stderr, "expr: warning: %d item(s) left on stack\n", sp);
    return 0;
}

Compile and Test

gcc -ansi -Wall ex5-10.c -o expr
./expr 2 3 4 + '*'      # 14  (quote * in shell)
./expr 10 2 /           # 5
./expr 3 4 '*' 2 +      # 14
./expr 1 2 + 3 4 + '*'  # 21

Sample Output

14
5
14
21

Note: Shell treats * as a glob wildcard — quote it: '*' or \*.

What This Exercise Teaches

  • argv as a token stream — command-line arguments are already tokenised by the shell; no lexer needed when each token is a separate argument
  • Pointer to pointer arithmeticargv++ advances the array-of-pointers; **argv is the first character of the current argument; (*argv)[1] is the second character (used to detect single-char operators vs numbers like -3)
  • Operator detection heuristic — checking (*argv)[1] == '\0' identifies one-character strings (the operators); everything else is passed to atof

Set Up Your C Environment

← Exercise 5-9  | 
Chapter 5 Solutions  | 
Exercise 5-11 →

Book: The C Programming Language, 2nd Ed — Kernighan & Ritchie

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>