K&R C Programs Exercise 7-5

Exercise 7-5. Rewrite the postfix calculator of Chapter 4 to use scanf and/or sscanf to do the input and number conversion.

The original Chapter 4 calculator used a custom getop() that read characters one at a time with getch/ungetch to recognise numbers vs operators. With scanf we replace that with a simpler approach: read one whitespace-delimited token with scanf("%s",...), then use sscanf to test whether it is a number.

Solution

/* K&R Exercise 7-5 — postfix RPN calculator using scanf
 * Compile: gcc -ansi -Wall ex7-5.c -o rpn
 * Usage:   echo "3 4 + 2 * ." | ./rpn     -> 14 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>

#define MAXSTACK 100
#define MAXTOKEN 100

static double stack[MAXSTACK];
static int    sp = 0;

static void push(double val)
{
    if (sp < MAXSTACK)
        stack[sp++] = val;
    else
        fprintf(stderr, "error: stack overflow\n");
}

static double pop(void)
{
    if (sp > 0)
        return stack[--sp];
    fprintf(stderr, "error: stack underflow\n");
    return 0.0;
}

int main(void)
{
    char   token[MAXTOKEN];
    double val;
    double a, b;

    while (scanf("%99s", token) == 1) {
        if (sscanf(token, "%lf", &val) == 1) {
            push(val);
        } else if (strlen(token) == 1) {
            switch (token[0]) {
            case '+': push(pop() + pop());          break;
            case '*': push(pop() * pop());          break;
            case '-': b = pop(); a = pop(); push(a - b); break;
            case '/': b = pop(); a = pop();
                      if (b == 0.0) fprintf(stderr, "error: divide by zero\n");
                      else push(a / b);
                      break;
            case '%': b = pop(); a = pop();
                      if (b == 0.0) fprintf(stderr, "error: modulo by zero\n");
                      else push(fmod(a, b));
                      break;
            case '.': printf("\t%.8g\n", pop()); break;
            default:
                fprintf(stderr, "unknown operator: %s\n", token);
                break;
            }
        } else {
            fprintf(stderr, "unknown token: %s\n", token);
        }
    }
    return 0;
}

Compile and Test

gcc -ansi -Wall ex7-5.c -o rpn -lm

echo "3 4 + ."            | ./rpn     # 7
echo "3 4 + 2 * ."        | ./rpn     # 14
echo "15 7 1 1 + - / 3 * 2 1 1 + + - ." | ./rpn   # 5
echo "10 3 % ."           | ./rpn     # 1

Expected Output

        7
        14
        5
        1

How scanf Simplifies the Input

The original getop() loop:

  • Read one character at a time
  • Push back non-digit characters with ungetch
  • Accumulate digit characters into a buffer
  • Return NUMBER vs operator code

With scanf:

/* One call reads the next whitespace-delimited token */
scanf("%99s", token);
/* One call tests if it is a number */
sscanf(token, "%lf", &val);

sscanf returns 1 on success (full number parsed) or 0 if the string isn’t a valid number — exactly the switch we need.

What This Exercise Teaches

  • sscanf as a type test — trying sscanf(token, "%lf", &val) and checking the return value cleanly replaces manual character-by-character digit detection
  • Non-commutativity of subtraction and division3 4 - means 3−4, not 4−3; the operands must be popped in order: b = pop(); a = pop(); push(a - b)
  • Width limit in scanf"%99s" caps the read at 99 characters, preventing overflow into the 100-byte token buffer; never use bare "%s" with an unknown-length input

Set Up Your C Environment

← Exercise 7-4  | 
Chapter 7 Solutions  | 
Exercise 7-6 →

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>