Exercise 4-3. Given the basic framework, it’s straightforward to extend the calculator. Add the modulus (
%) operator and provisions for negative numbers.
This exercise extends the RPN desk calculator from K&R Section 4.3. Two independent additions:
- Modulus operator
%— pop two operands, cast toint, apply%, push result. Floating-point modulus is mathematically well-defined but%in C only works on integers, so truncation is required. - Negative number input —
getopmust distinguish a unary minus (start of a negative number) from the binary minus operator. The rule: if-is immediately followed by a digit or., it is part of a number; otherwise it is an operator.
Key Changes to the Calculator
/* K&R Exercise 4-3 — RPN calculator: % operator and negative numbers
* Compile: gcc -ansi -Wall ex4-3.c -o ex4-3 */
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <math.h>
#define MAXOP 100
#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("error: stack full\n");
}
double pop(void) {
if (sp > 0) return val[--sp];
printf("error: stack empty\n"); return 0.0;
}
static int buf = -1; /* ungetch buffer */
int getch(void) { return (buf >= 0) ? (buf = -1, buf+1-1, buf = -1, getchar()) : getchar(); }
/* simpler: */
static int _buf = EOF;
int gc(void) { int c = _buf; _buf = EOF; return (c != EOF) ? c : getchar(); }
void ugc(int c) { _buf = c; }
/* getop — returns NUMBER for a number token, else the operator char.
* Handles negative numbers: if '-' is followed by digit or '.', it's a number. */
int getop(char s[])
{
int i, c, next;
while ((s[0] = c = gc()) == ' ' || c == '\t')
;
s[1] = '\0';
if (!isdigit(c) && c != '.' && c != '-')
return c;
if (c == '-') {
next = gc();
if (!isdigit(next) && next != '.') {
ugc(next);
return '-'; /* binary minus operator */
}
/* unary minus: fall through, prepend '-' to the number */
s[1] = c = next;
i = 1;
} else {
i = 0;
}
if (isdigit(c))
while (isdigit(s[++i] = c = gc()))
;
if (c == '.')
while (isdigit(s[++i] = c = gc()))
;
s[i] = '\0';
if (c != EOF) ugc(c);
return NUMBER;
}
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 '%': /* Exercise 4-3: modulus */
op2 = pop();
if (op2 != 0.0) push((int)pop() % (int)op2);
else printf("error: zero divisor\n");
break;
case '\n':
printf("\t%.8g\n", pop());
break;
default:
printf("error: unknown command %c\n", type);
break;
}
}
return 0;
}
Test It
echo "10 3 %" | ./ex4-3 # 1
echo "-5 2 +" | ./ex4-3 # -3
echo "-3.14 2 *" | ./ex4-3 # -6.28
Sample Output
1
-3
-6.28
What This Exercise Teaches
- Context-sensitive lexing — the same
-character means two different things; one lookahead character resolves the ambiguity - Float-to-int truncation for % — C’s
%requires integer operands; casting truncates toward zero, which is the standard C99+ behaviour - Operator vs number parsing boundary — the key insight is that
getopmust peek ahead and push back unused characters
Set Up Your C Environment
← Exercise 4-2 |
Chapter 4 Solutions |
Exercise 4-4 →
Book: The C Programming Language, 2nd Ed — Kernighan & Ritchie