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 + *evaluates2 * (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
argvas 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 arithmetic —
argv++advances the array-of-pointers;**argvis 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 toatof
Set Up Your C Environment
← Exercise 5-9 |
Chapter 5 Solutions |
Exercise 5-11 →
Book: The C Programming Language, 2nd Ed — Kernighan & Ritchie