Thread: K&R Ch4 Polish calculator, two questions

  1. #1
    Registered User
    Join Date
    May 2011
    Posts
    36

    K&R Ch4 Polish calculator, two questions

    The entire code below for reference:

    Whenever I try out a code sample from a book like this, I always make it a point to break it. The "MAXOP" defined in the book was 100. So for grins I changed it to 5 and tried using operands of more than 5 digits. Notice the other line I highlighted in red where I print out the array for inspection. This array is supposedly 5 char max, and yet I am able to get it to work with like ~17-digit operands and even print all of them back as part of the string/array. After 17/18 digits, it stops printing... but still, shouldn't it stop printing (and give me an error before that) at 5?

    The second issue, is that the author puts a BUFSIZE of 100 on his ungetch() buffer... but as far as I can tell there is no way to get more than 1 character full. No matter what you do, it will only fill one character up, and that character will always empty before another one has a chance to go in. Am I missing something, or is the BUFSIZE of 100 totally unnecessary? Is it even possible to see the "ungetch: too many characters" error message?

    Code:
    #include <stdio.h>
    #include <stdlib.h> /* for atof() */
    #define MAXOP 5 /* max size of operand or operator */
    #define NUMBER '0' /* signal that a number was found */
    int getop(char []);
    void push(double);
    double pop(void);
    /* reverse Polish calculator */
    main()
    {
    	int type;
    	double op2;
    	char s[MAXOP];
    	while ((type = getop(s)) != EOF) {
    		switch (type) {
    			case NUMBER:
    				printf("s = %s\n", s);
    				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 '\n':
    				printf("\t%.8g\n", pop());
    				break;
    			default:
    				printf("error: unknown command %s\n", s);
    				break;
    		}
    	}
    	return 0;
    }
    
    #define MAXVAL 100 /* maximum depth of val stack */
    int sp = 0; /* next free stack position */
    double val[MAXVAL]; /* value stack */
    /* push: push f onto value stack */
    void push(double f)
    {
    	if (sp < MAXVAL)
    		val[sp++] = f;
    	else
    		printf("error: stack full, can't push %g\n", f);
    }
    /* pop: pop and return top value from stack */
    double pop(void)
    {
    	if (sp > 0)
    		return val[--sp];
    	else {
    		printf("error: stack empty\n");
    		return 0.0;
    	}
    }
    
    #include <ctype.h>
    int getch(void);
    void ungetch(int);
    /* getop: get next character or numeric operand */
    int getop(char s[])
    {
    	int i, c;
    	while ((s[0] = c = getch()) == ' ' || c == '\t')
    		;
    	s[1] = '\0';
    	if (!isdigit(c) && c != '.')
    		return c; /* not a number */
    	i = 0;
    	if (isdigit(c)) /* collect integer part */
    		while (isdigit(s[++i] = c = getch()))
    			;
    	if (c == '.') /* collect fraction part */
    		while (isdigit(s[++i] = c = getch()))
    			;
    	s[i] = '\0';
    	if (c != EOF)
    		ungetch(c);
    	return NUMBER;
    }
    
    #define BUFSIZE 100
    char buf[BUFSIZE]; /* buffer for ungetch */
    int bufp = 0; /* next free position in buf */
    int getch(void) /* get a (possibly pushed-back) character */
    {
    	return (bufp > 0) ? buf[--bufp] : getchar();
    }
    void ungetch(int c) /* push character back on input */
    {
    	if (bufp >= BUFSIZE)
    		printf("ungetch: too many characters\n");
    	else
    		buf[bufp++] = c;
    }

  2. #2
    Registered User
    Join Date
    Jun 2011
    Posts
    4,513
    The "MAXOP" defined in the book was 100. So for grins I changed it to 5 and tried using operands of more than 5 digits. Notice the other line I highlighted in red where I print out the array for inspection. This array is supposedly 5 char max, and yet I am able to get it to work with like ~17-digit operands and even print all of them back as part of the string/array.
    It's probably because the memory allocated by your program is arranged in such a way that your over-writes to the array just happen to result in an expected otuput.

    This is, however, technically undefined behavior and there is no guarantee that you will get these results. For good code, you, the programmer, have to tame these variables so they're always under strict control.

  3. #3
    Banned
    Join Date
    Aug 2010
    Location
    Ontario Canada
    Posts
    9,547
    Quote Originally Posted by The111 View Post
    Whenever I try out a code sample from a book like this, I always make it a point to break it. The "MAXOP" defined in the book was 100. So for grins I changed it to 5 and tried using operands of more than 5 digits. Notice the other line I highlighted in red where I print out the array for inspection. This array is supposedly 5 char max, and yet I am able to get it to work with like ~17-digit operands and even print all of them back as part of the string/array. After 17/18 digits, it stops printing... but still, shouldn't it stop printing (and give me an error before that) at 5?
    Dumb Luck... and not much more. Very likely your s array is sitting at the end of the stack where you can go out of bounds without crashing the program... but this is clearly undefined behaviour which means that just about any screwy thing can happen because of it.

  4. #4
    Registered User
    Join Date
    May 2011
    Location
    Around 8.3 light-minutes from the Sun
    Posts
    1,949
    Here is a nice explanation on what undefined behavior means. Note section 3.4 specifically talks about buffer overruns.
    Quote Originally Posted by anduril462 View Post
    Now, please, for the love of all things good and holy, think about what you're doing! Don't just run around willy-nilly, coding like a drunk two-year-old....
    Quote Originally Posted by quzah View Post
    ..... Just don't be surprised when I say you aren't using standard C anymore, and as such,are off in your own little universe that I will completely disregard.
    Warning: Some or all of my posted code may be non-standard and as such should not be used and in no case looked at.

  5. #5
    Registered User
    Join Date
    May 2011
    Posts
    36
    Awesome, thanks for the explanations all, and thanks for the link Andrew. I had never heard the term undefined behavior before, but it makes sense.

    Any thoughts on the second half of my question? (how to trigger the ungetch() error?)

    Thanks!

  6. #6
    Banned
    Join Date
    Aug 2010
    Location
    Ontario Canada
    Posts
    9,547
    First of all... clean up your code... don't be scattering #include and variable declarations around like a drunken sailor... C-99 may let you do that but it doesn't mean you should.

    Second... I'm betting your input functions would work a lot better if you read the input as a string (fgets()) and then parsed the string instead of trying to manipulate the input buffer.

  7. #7
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,420
    Quote Originally Posted by CommonTater
    First of all... clean up your code... don't be scattering #include and variable declarations around like a drunken sailor... C-99 may let you do that but it doesn't mean you should.
    Actually, the C99 rules that you're talking about have to do with local variables. The declarations that you're criticising are for global variables...

    Oh, and The111, avoid global variables. Granted, you may just be copying the author who may have used them out of convenience, but until you can properly recognise when they are appropriate, it is best to stick to local variables.
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  8. #8
    Banned
    Join Date
    Aug 2010
    Location
    Ontario Canada
    Posts
    9,547
    Quote Originally Posted by laserlight View Post
    Actually, the C99 rules that you're talking about have to do with local variables. The declarations that you're criticising are for global variables...

    Oh, and The111, avoid global variables. Granted, you may just be copying the author who may have used them out of convenience, but until you can properly recognise when they are appropriate, it is best to stick to local variables.
    Point taken... but still, the OP would be smart to clean up his code. I've always found it easiest to list variables at the top of a page and add comments about their purpose. That way, as I read down the page I have a much better idea what's going on.

  9. #9
    Registered User
    Join Date
    May 2011
    Posts
    36
    Quote Originally Posted by CommonTater View Post
    Second... I'm betting your input functions would work a lot better if you read the input as a string (fgets()) and then parsed the string instead of trying to manipulate the input buffer.
    I literally thought the same thing myself.

    However, my weakest skill at this point in time is reading other people's code, and I figure with a real job in software that is something I'll have to be good at, so I am trying to understand the example code as given. And I still can't understand how I could possibly trigger that error message (too many chars) for ungetch().

    As far your other advice... point taken. When I write code from scratch it tends to be more organized, in this case I was just cutting and pasting from the book quickly to get it all together in one source file. Out of consideration for readers here though, I probably should have cleaned it up.

  10. #10
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,420
    Quote Originally Posted by CommonTater
    I've always found it easiest to list variables at the top of a page and add comments about their purpose. That way, as I read down the page I have a much better idea what's going on.
    I agree, but then that's because global variables are... global, so placing their declarations in between functions does not really make sense since there is no declaration near first use.

    Quote Originally Posted by The111
    However, my weakest skill at this point in time is reading other people's code, and I figure with a real job in software that is something I'll have to be good at, so I am trying to understand the example code as given.
    No matter how good to get, you'll find that your skill in reading other people's code will remain weaker than your skill at writing code. So yes, work on it, but don't worry about it. Oh, for practice try reading CommonTater's code: he has a horrible, though arguably acceptable, uncommon format style. I don't recommend trying to edit his code without re-formatting first though, otherwise you'll be slammed by colleagues should you adopt his style.
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  11. #11
    Registered User
    Join Date
    May 2011
    Location
    Around 8.3 light-minutes from the Sun
    Posts
    1,949
    Quote Originally Posted by The111 View Post
    However, my weakest skill at this point in time is reading other people's code, and I figure with a real job in software that is something I'll have to be good at, so I am trying to understand the example code as given.
    That is the hardest thing for any programmer to do. It is good that you are working on that. Another good way to practice this skill is by hanging around this board, you are guaranteed to see just about every possible combination.

    Quote Originally Posted by The111 View Post
    As far your other advice... point taken. When I write code from scratch it tends to be more organized, in this case I was just cutting and pasting from the book quickly to get it all together in one source file. Out of consideration for readers here though, I probably should have cleaned it up.
    As long as you understand why doing such things in a real program would not be beneficial. In the case of K&R, as well as just about every other teaching book, the authors present things so as to not convolute the lesson with syntatical niceties. This is important to keep in mind as you develop your own programming style.
    Quote Originally Posted by anduril462 View Post
    Now, please, for the love of all things good and holy, think about what you're doing! Don't just run around willy-nilly, coding like a drunk two-year-old....
    Quote Originally Posted by quzah View Post
    ..... Just don't be surprised when I say you aren't using standard C anymore, and as such,are off in your own little universe that I will completely disregard.
    Warning: Some or all of my posted code may be non-standard and as such should not be used and in no case looked at.

  12. #12
    Banned
    Join Date
    Aug 2010
    Location
    Ontario Canada
    Posts
    9,547
    Quote Originally Posted by The111 View Post
    I literally thought the same thing myself.

    However, my weakest skill at this point in time is reading other people's code, and I figure with a real job in software that is something I'll have to be good at, so I am trying to understand the example code as given. And I still can't understand how I could possibly trigger that error message (too many chars) for ungetch().
    Check your compiler docs... you may be on a compiler or OS version that only supports single characters for ungetch().
    This is from the Pelles C, help file...

    _ungetch function [not standard C]


    Purpose:
    Pushes a character back onto the console.


    Syntax:
    int _ungetch(int c);


    Declared in:
    <conio.h>


    Description:
    The _ungetch function pushes the character c back onto the console, causing the character to be the next character read by the _getch or _getche function. Only one character may be pushed back before the next read. The character c may not be EOF.


    Returns:
    The character pushed back, or EOF in case of error.
    Note that this is an older function, now branded "not standard" by the C-99 rules...

    What compiler are you using?

  13. #13
    Banned
    Join Date
    Aug 2010
    Location
    Ontario Canada
    Posts
    9,547
    Quote Originally Posted by AndrewHunter View Post
    That is the hardest thing for any programmer to do. It is good that you are working on that. Another good way to practice this skill is by hanging around this board, you are guaranteed to see just about every possible combination.
    LOL... including a few impossible ones!

  14. #14
    Registered User
    Join Date
    May 2011
    Location
    Around 8.3 light-minutes from the Sun
    Posts
    1,949
    Quote Originally Posted by CommonTater View Post
    LOL... including a few impossible ones!
    Haha...isn't that the truth. I think that is where you can learn the most though.
    Quote Originally Posted by anduril462 View Post
    Now, please, for the love of all things good and holy, think about what you're doing! Don't just run around willy-nilly, coding like a drunk two-year-old....
    Quote Originally Posted by quzah View Post
    ..... Just don't be surprised when I say you aren't using standard C anymore, and as such,are off in your own little universe that I will completely disregard.
    Warning: Some or all of my posted code may be non-standard and as such should not be used and in no case looked at.

  15. #15
    and the Hat of Guessing tabstop's Avatar
    Join Date
    Nov 2007
    Posts
    14,336
    Quote Originally Posted by CommonTater View Post
    Note that this is an older function, now branded "not standard" by the C-99 rules...
    So why not use the standard ungetc? (It was in C89 too, as I recall, so it should be pretty much everywhere.)

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. polish calculator
    By c_lady in forum C Programming
    Replies: 14
    Last Post: 04-09-2010, 04:04 PM
  2. reverse polish calculator
    By Tool in forum C Programming
    Replies: 1
    Last Post: 12-24-2009, 05:25 PM
  3. Inverse Polish Calculator
    By Sailors in forum C Programming
    Replies: 0
    Last Post: 07-30-2007, 04:51 PM
  4. Reverse Polish Calculator
    By BlasterStar in forum C++ Programming
    Replies: 3
    Last Post: 01-22-2003, 11:12 PM
  5. c++ Reverse Polish Calculator Help
    By knight101 in forum C++ Programming
    Replies: 5
    Last Post: 11-12-2001, 09:31 AM