aboutsummaryrefslogtreecommitdiff
path: root/ch7
diff options
context:
space:
mode:
authorzlg <zlg@zlg.space>2016-10-12 06:49:26 -0700
committerzlg <zlg@zlg.space>2016-10-12 06:49:26 -0700
commitedf72affee48c3e6530d6f60142d834eecae8aa3 (patch)
tree9bb5cdb1ccf5bad45c9c3351bcd78efd3d049932 /ch7
parentSolve Exercise 7-4: minscanf() (diff)
downloadknr-edf72affee48c3e6530d6f60142d834eecae8aa3.tar.gz
knr-edf72affee48c3e6530d6f60142d834eecae8aa3.tar.bz2
knr-edf72affee48c3e6530d6f60142d834eecae8aa3.tar.xz
knr-edf72affee48c3e6530d6f60142d834eecae8aa3.zip
Solve Exercise 7-5: RPN calculator with scanf()
This exercise was deceptively simple, and ended up simpler than the original! The *scanf series of functions is very useful, but you also have to be careful or you'll run into segfaults.
Diffstat (limited to 'ch7')
-rw-r--r--ch7/7-05_postfix-calc-scanf.c114
1 files changed, 114 insertions, 0 deletions
diff --git a/ch7/7-05_postfix-calc-scanf.c b/ch7/7-05_postfix-calc-scanf.c
new file mode 100644
index 0000000..161f5f0
--- /dev/null
+++ b/ch7/7-05_postfix-calc-scanf.c
@@ -0,0 +1,114 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <math.h>
+#include <string.h>
+
+/* The C Programming Language: 2nd Edition
+ *
+ * Exercise 7-5: Rewrite the postfix calculator of Chapter 4 to use scanf
+ * and/or sscanf to do the input and number conversion.
+ *
+ * Notes:
+ * scanf wants allocated memory of some sort, meaning you're either going
+ * to pass it a pointer (with malloc()'d memory) or you're passing a variable
+ * with the unary & (address-of) operator. Failing to use scanf correctly
+ * _will_ create runtime problems! gcc won't tell you what happens without
+ * -Wall and -Wextra, but scanf segfaults by trying to access an uninitialized
+ * pointer.
+ *
+ * Thus there are two ways to use scanf:
+ *
+ * int foo = 0;
+ * scanf("%d", &foo);
+ * // do stuff
+ *
+ * -- or --
+ *
+ * int *foo = malloc(sizeof(int));
+ * scanf("%d", foo);
+ * // do stuff
+ * free(foo);
+ *
+ * For the sake of sanity in an easy exercise, I'll be using the former form
+ * since a few values on the stack won't hurt anything.
+ */
+
+#define MAXOP 100
+#define MAXVAL 100
+#define BUFSIZE 100
+
+void push(double);
+double pop(void);
+
+int sp = 0; // Next free stack position
+double val[MAXVAL]; // Value stack
+char buf[BUFSIZE]; // buffer
+int bufp = 0; // next free position in buf
+double vars[27];
+
+/* Reverse Polish calculator:
+ *
+ * Binary operations (+-*\)
+ * operand operand operator
+ *
+ * Example: 6 minus 2 in Reverse Polish Notation is "6 2 -"
+ */
+int main() {
+ char *c;
+ double a = 0, op2;
+ char s[MAXOP];
+ char ltr = '\0';
+ while (scanf("%s%c", s, &ltr) == 2) {
+ if (sscanf(s, "%lf", &a) == 1) {
+ push(a);
+ } else if (sscanf(s, "%s", buf)) {
+ for (c = buf; *c; c++) {
+ switch (*c) {
+ 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: Cannot divide by zero.\n");
+ }
+ break;
+ default:
+ printf("Error: Unknown command %s\n", s);
+ break;
+ }
+ }
+ if (ltr == '\n') {
+ printf("%.8f\n", pop());
+ }
+ }
+ }
+ return 0;
+}
+
+void push(double f) {
+ if (sp < MAXVAL) {
+ val[sp++] = f;
+ } else {
+ printf("Error: Stack full. Cannot push %g\n", f);
+ }
+}
+
+double pop(void) {
+ if (sp > 0) {
+ return val[--sp];
+ } else {
+ printf("Error: Stack empty.\n");
+ return 0.0;
+ }
+}