K&R C Programs Exercise 4-10

Exercise 4-10. An alternate organization uses getline to read an entire input line; this makes getch and ungetch unnecessary. Revise the calculator to use this approach.

The original calculator reads the input one character at a time via getch/ungetch. This exercise replaces that machinery with a line-buffer approach: read the whole line into a static array, then have getop scan through it using an index. No pushback is needed because backing up the index is free — just decrement it. The tradeoff: the line-buffer approach is simpler but loses the ability to handle multi-line tokens; the original approach works with any input stream.

Solution

/* K&R Exercise 4-10 — RPN calculator using getline (no getch/ungetch)
 * Compile: gcc -ansi -Wall ex4-10.c -o ex4-10 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

#define MAXOP   100
#define MAXLINE 1000
#define NUMBER  '0'
#define MAXVAL  100

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

void   push(double f) { if (sp < MAXVAL) val[sp++] = f; else printf("stack full\n"); }
double pop(void)      { return sp > 0 ? val[--sp] : (printf("stack empty\n"), 0.0); }

/* Line buffer — getop scans through it via an index */
static char line[MAXLINE];
static int  li = 0;   /* next position to read in line[] */

int getline_buf(void)
{
    if (fgets(line, MAXLINE, stdin) == NULL) {
        line[0] = '\0';
        return 0;
    }
    li = 0;
    return 1;
}

/* getop using line buffer index — no getch/ungetch */
int getop(char s[])
{
    int i, c;

    /* skip whitespace; if we reach end of line, read a new one */
    while (line[li] == ' ' || line[li] == '\t')
        li++;

    if (line[li] == '\0' || line[li] == '\n') {
        if (!getline_buf()) return EOF;
        return '\n';
    }

    s[0] = c = line[li++];
    s[1] = '\0';

    if (!isdigit(c) && c != '.')
        return c;

    i = 0;
    if (isdigit(c))
        while (isdigit(s[++i] = c = line[li++]))
            ;
    if (c == '.')
        while (isdigit(s[++i] = c = line[li++]))
            ;
    s[i] = '\0';
    if (c != '\0') li--;   /* push back: just decrement the index */
    return NUMBER;
}

int main(void)
{
    int type;
    double op2;
    char s[MAXOP];

    if (!getline_buf()) return 0;

    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) push(pop()/op2); break;
        case '\n':   printf("\t%.8g\n", pop()); break;
        default:     printf("unknown: %c\n", type); break;
        }
    }
    return 0;
}

Compile and Test

gcc -ansi -Wall ex4-10.c -o ex4-10
echo "3 4 + 2 *" | ./ex4-10   # 14
echo "10 2 /"   | ./ex4-10    # 5

Sample Output

        14
        5

What This Exercise Teaches

  • Two design approaches for lexing — character stream (getch/ungetch, works with any stream, handles multi-line tokens) vs line buffer (simpler index arithmetic, “pushback” is free)
  • Pushback without a buffer — decrementing an index is equivalent to ungetch; only valid because the character was already in the buffer
  • fgets vs getcharfgets reads a whole line at once; it also null-terminates, making index scanning safe without a sentinel check

Set Up Your C Environment

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

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

1 comment on “K&R C Programs Exercise 4-10

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>