-1

I have been reading through the C programming Langauge book and in section 4.3 I am trying to compile and run the code thats provided in pages 76-79. I think I have done exactly what is in the textbook, but its not working as it should.

From some printf's i have found that the numbers that are inputted are not actually being pushed to the stack in val[sp]. instead, when printing out the number, it shows a 0.

Also, the spaces aren't being skipped in

while((s[0] = c = getch()) == ' ' || c == '\t'{
    ;
}

and so the getop() treats them as a non digit and just returns the space. Because of this the output just prints error unknown command when the spaces are being read.

Also, the '+' is not being read as a command! So it just returns error unknown command when the program encounters it. I am at a loss as how this is happening given I have copied word for word in the textbook and ran it. I am not sure if this code is out of date and doesn't run on newer machines as well. I am also using CodeBlocks to run this if that helps.

Any help is greatly appreciated!

Here's my code

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <ctype.h>


#define MAXOP 100
#define NUMBER '0'


int getop(char[]);
void push(double);
double pop(void);




int main()
{
    //extern double val[MAXVAL];
    int type;
    double op2;
    char s[MAXOP];

    while((type = getop(s)) != EOF){
        switch(type){
        case NUMBER:
            puts("a 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 division");
            }
            break;

        case '\n':
            printf("\t%.8g\n", pop());
            break;
        default:
            printf("error unknown command %s\n", s);
            break;

        }
    }
    printf("Hello world!\n");
    return 0;
}

#define MAXVAL 100
int sp = 0;
double val[MAXVAL];

void push(double f)
{
    //extern int sp;
    //extern double val[MAXVAL];

    if(sp < MAXVAL){
        val[++sp] = f;
    }
    else{
        printf("stack full, can't push\n");
    }
}

double pop(void)
{
    //extern int sp;
    //extern double val[MAXVAL];

    if(sp > 0){
        return val[--sp];
    }
    else{
        printf("stack is empty\n");
        return 0.0;
    }
}

int getch(void);
void ungetch(int c);

int getop(char s[])
{
    int i, c;

    while((s[0] = c = getch()) == ' ' || c == '\t'){
        ;
    }

    s[1] = '\0';
    if(!isdigit(c) && c != '.'){
        printf("%c", c);
        return c;
    }
    i = 0;
    if(isdigit(c)){
        while(isdigit(s[i++] = c = getch())){
            ;
        }
    }
    if(c == '.'){
        while(isdigit(s[i++] = c = getch())){
            ;
        }
    }
    s[i] = '\0';
    if(c != EOF){
        ungetch(c);
    }
    return NUMBER;
}

#define BUFSIZE 100
char buf[BUFSIZE];
int bufp = 0;

int getch(void)
{
    //extern int bufp;
    //extern char buf[BUFSIZE];

    return (bufp > 0) ? buf[--bufp] : getchar();
}

void ungetch(int c)
{
    //extern int bufp;
    //extern char buf[BUFSIZE];

    if(bufp >= BUFSIZE){
        printf("ungetch: too many characters\n");
    }
    else{
        buf[++bufp] = c;
    }
}

and the output for 3 4 + 1

I have tried to print out each part (the number when it is being pushed) and also print out the '+' to see if it is actually being returned as '+'. It is, and I am at a loss as how this is happening.

theperson
  • 3
  • 4
  • 3
    There are far better books if you want to learn non-K&R C, as in C that people actually use. Find one that at *least* covers C99. This book is interesting from a historical perspective, but its value for learning is dubious at best. The 2nd Edition is from 1988, and C99 itself is already going on 24 years old. There's also C11 and C17! – tadman Jan 31 '23 at 02:10
  • 1
    Fair enough and I have realised that. At this point I'm a beginner in C, I'm trying to just go through this book and then to others that have C99. For me personally, chopping and changing books only confuses me as someone who doesnt know what to focus on because, as a beginner, I don't have the perspective of what details and areas of the language are important. I've seen that 'Effective C' and 'C programming a modern approach' seem to be something thats recommended a lot. – theperson Jan 31 '23 at 02:16
  • It's probably not everything, but _stack operations_ are always "pre-increment/post-decrement" for push/pop, or vice versa... Your stack operations seem to both be "pre-" for pushing and popping... This is not right... – Fe2O3 Jan 31 '23 at 02:21
  • 1
    I get where you're coming from, but the K&R books in particular are notorious for being full of obsolete methods and other K&R-isms that needlessly complicate your learning, it's even described, delicately, as "parts of this book...show its age" in the [definitive C book list](https://stackoverflow.com/questions/562303/the-definitive-c-book-guide-and-list) here. I think you'd find *Programming in C* by Kochlan or something published in the last 15 years to be more up-to-date. Missing out on 40+ years of improvements is a huge disservice to your learning. – tadman Jan 31 '23 at 02:26
  • *Effective C* also sounds like a great choice, as it seems solidly rooted in modern C practices and has the benefit of decades of experience the K&R book did not. The reason I'm being overly cautious here is C is an extremely *dangerous* language to use incorrectly, it has zero safety measures. If you ask it to do something, it will just do it, consequences be damned, assuming the programmer knows what they're doing. This is why it's important to write code that's understandable first, more than it is purely functional and/or compiles. – tadman Jan 31 '23 at 02:27
  • 3
    @tadman I just found, downloaded the PDF and checked the section 4.3 (hardcopy page #77) that the OP has typed... Sure 'nuff... The book shows "post increment" on "push" and "pre decrement" on "pop"... A problem with "new & improved" is that they are often still collecting their errata. – Fe2O3 Jan 31 '23 at 02:36
  • 2
    I don't think I've seen a more convoluted program for simple arithmetic operations as what was contrived here. While there is certainly good learning involved in tracing the inputs, indexes and pointer operations throughout the code -- that's where the benefit ends. For someone new to C -- this is one of the worst examples for a beginner of how to handle input, stacks and input parsing that I've seen in the near decade I've answered questions here. @tadman provides good advice in perhaps a change of books. Then revisit this later when you are comfortable with C. – David C. Rankin Jan 31 '23 at 02:37
  • 2
    @DavidC.Rankin A lot of the K&R code is intended to show off what the language *could* do, not what a programmer *should* write. There's an implied "Try this with FORTRAN! How about that COBOL!" in some of the examples. – tadman Jan 31 '23 at 02:47
  • As alluded to, `val[++sp] = f;` should be `val[sp++] = f;`, and `buf[++bufp] = c;` should be `buf[bufp++] = c;`. Voting to close as a typo. – Oka Jan 31 '23 at 03:03
  • I've often wondered what the main point of some of the K&R examples was. Yes, you are introduced to the features of the language, but they are constructed in a bewildering way. Take the use of use of the *ternary* in the `getch()` return. Fine, but for someone new to C - mind-numbingly complex. The same for the compound assignment in `main()` and in the `getops()` calls. Then there is the use of `atof()` and `NUMBER` as the digit `'0'` instead of `0` -- again, all fine, but woe be the new programmer digesting it `:)` Moreover, run this through `gdb` - not an intro lesson. – David C. Rankin Jan 31 '23 at 03:06
  • 1
    @tadman thanks for the reccomendations. You're right, The C programming Language is a pretty old book and others will have had much more effective methods thats been tried and tested in the last 40 years. I'll check out effective c as well. – theperson Jan 31 '23 at 03:08
  • 1
    @Oka I've actually tried both ways to no avail. Thanks for taking the time to look through it! – theperson Jan 31 '23 at 03:11
  • 1
    Both instances of `isdigit(s[i++] = c = getch())` should be `isdigit(s[++i] = c = getch())`. Fixing these, and the previously mentioned typos cause this program to behave correctly, AFAICT. Still voting to close as a typo. – Oka Jan 31 '23 at 03:50
  • 1
    If you are unaware of the difference between *prefix* and *postfix* increment and decrement operators, please refer to [Increment/decrement operators](https://en.cppreference.com/w/c/language/operator_incdec). – Oka Jan 31 '23 at 03:55
  • 1
    And well if you would post code like `while((s[0] = c = getch()) == ' ' || c == '\t')` (taken straight from K&R) on a professional code review, you'd be dismissed as a clueless quack and risk losing your job. The problem isn't just that the book is old, the main problem is that it's _bad_, teaching _bad practices_ that weren't acceptable even in the 1970s. There were no excuses for writing obfuscated crap code like that even in the 1970s. People need to stop focusing on how old the book is, that's not even the core problem, never was. – Lundin Jan 31 '23 at 08:58
  • 1
    @tadman I learned C in 1983 using Version 1 of the C Programing Language by K&R. You are basically correct about a lot of things. The comparisons to FORTRAN would have been to FORTRAN 66 or FORTRAN 77 which are both obsolete as well. For COBOL there might be one or two banks still using it, but it has been basically obsolete since 2000. I doubt anyone is using ISAM or VSAM based databases anymore. A book recommended for C by UC Irvines continuing education classes is `C A Reference Manual 5th edition` by `Samuel P Harbison III` and `Guy L. Steele Jr.`. – pacmaninbw Jan 31 '23 at 15:57

1 Answers1

2

One problem is that your code has:

void ungetch(int c)
{
    //extern int bufp;
    //extern char buf[BUFSIZE];

    if(bufp >= BUFSIZE){
        printf("ungetch: too many characters\n");
    }
    else{
        buf[++bufp] = c;
    }
}

The book (p79) has:

void ungetch(int c)
{
    if(bufp >= BUFSIZE){
        printf("ungetch: too many characters\n");
    }
    else{
        buf[bufp++] = c;
    }
}

The difference between ++bufp and bufp++ is critical.

It isn't the only problem, but things are closer to working when that's fixed.

  • You are consistently rewriting ++x as x++ and x++ as ++x, and the expressions are not interchangeable. It is very, very important to get those correct.

  • As pointed out by Oka, in the getop() function, you have (twice):

    while(isdigit(s[i++] = c = getch())){
        ;
    }
    

    where the book has:

    while (isdigit(s[++i] = c = getch()))
        ;
    
  • In the push() function, you have:

    val[++sp] = f;
    

    where the book has:

    val[sp++] = f;
    

It is crucial that:

  1. You copy code accurately.
  2. You understand the difference between ++x and x++.

With those extra fixes — meaning changing your code to match what's in the book — the code seems to work, at least on the input 3 4 +.

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278