Jump to: navigation, search

The C Programming Language, 2nd Edition, by Kernighan and Ritchie
Exercise 4.11 on page 83

Modify getop so that it doesn't need to use ungetch. Hint: use an internal static variable.



Solution by Gregory Pietsch

#include <stdio.h>
#define NUMBER '0'      

int getop(char *s)
{
    int c;
    static int buf = EOF;

    if (buf != EOF && buf != ' ' && buf != '\t'
        && !isdigit(buf) && buf != '.') {
        c = buf;
        buf = EOF;
        return c;
    }
    if (buf == EOF || buf == ' ' || buf == '\t') 
        while ((*s = c = getch()) == ' ' || c == '\t')
            ;
    else 
        *s = c = buf;
    buf = EOF;
    *(s + 1) = '\0';
    if (!isdigit(c) && c != '.')
        return c;       /* not a number */
    if (isdigit(c))     /* collect integer part */
        while (isdigit(*++s = c = getch()))
            ;
    if (c == '.')       /* collect fraction part */
        while (isdigit(*++s = c = getch()))
            ;
    *++s = '\0';
    buf = c;
    return NUMBER;
}


Solution by Liangming

int getop(char s[])
{
    int i, c;
    static char buf = EOF;

    if (buf == EOF || buf == ' ' || buf == '\t') {
        while ((s[0] = c = getch()) == ' ' || c == '\t')
            ;
        s[1] = '\0';
    } else
        c = buf;
    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)
        buf = c;
    return NUMBER;
}


Solution by Sam Smolkin

#include <ctype.h>
#define NUMBER '0'      

/* getop: get next operator or numeric operand */
int getop(char s[])
{
  int i;
  static int c = ' ';
  
  while(c == ' ' || c == '\t') {
      c = getchar();
  }

  s[0] = c;
  s[1] = '\0';
  if(!isdigit(c) && c != '.') {
      int res = c;
      c = ' '; /* default behavior is to "unget" at end by using last read character (saved in static c) as first read character on the next call, but in this case we don't want to do so, since no extra character was read */
      return res; /* not a number */
  }
  i = 0;
  if(isdigit(c)) /* collect integer part */
    while(isdigit(s[++i] = c = getch()))
      ;
  if(c == '.')
    while(isdigit(s[++i] = c = getch()))
      ;
  s[i] = '\0';

  return NUMBER;
}

Solution by anonymous

The getop function is based on the solution from exercise 4.3 (getop can handle negative numbers). The logic based on Sam's solution since it seems error free and it required minimal changes.

To get rid of ungetch (the function that pushes the extra char read back into the read buffer) you can use an internal static variable to keep track of the last read char at the end of the function. Using an internal static variable allows you to make an external variable private and only accessible inside of a function. It is possible to use an external variable instead, but the hint implies you shouldn't for this exercise. Knowing this, modifying getop required a few small changes. First, I needed to convert c to a static int so the value persists each function call. I initialized it to a space char so it will call getch at least once during the first call to getop. Second, the while loop was updated to only call getch if c is currently white space. This is a necessary change since c is set to the last retrieved char from getch from the previous call to getop. If I left it as is, that char could be skipped causing problems. Third, I had to move the s[0] = c below the while loop since it was no longer in it. Fourth, I needed to update the way the first if statement returns the non-digit char so the value would be returned and c would be updated to prevent an infinite loop. Finally, I removed the ungetch call at the bottom. The rest is the same from my getop function in my exercise 4.3 solution.

#include <stdio.h> // for EOF
#include <ctype.h> // for isdigit
#include "calc.h"  // for getch() and NUMBER
/*
    Exercise 4-11. Modify getop so that it doesn't need to use ungetch. Hint: use an internal static variable
*/

// get next operator or numeric operand
int getop(char s[])
{
    int i;
    static int c = ' '; // used to keep track of last char in previous call to getch(). static keyword in a function causes c to persist after function call finishes and the value is retained.

    while (c == ' ' || c == '\t') // skip white space
        c = getch();
    s[0] = c; // set s[0] to first non-white space char from input
    s[1] = '\0'; // terminate string in case input is not a number (s is expected to be a string throughout program)
    if (!isdigit(c) && c != '.' && c != '-')
    {
        int toReturn = c; // save c value so it can be returned below without being reset
        c = ' ';          // reset c so it will "skip" white space and grab the next char from input (otherwise this could be an infinite loop if char causes an error)
        return toReturn;  // c is not a number. It is probably an operator, so return it. Minus operator is a special case and is handled right before return NUMBER;
    }
    i = 0;
    if (c == '-' || isdigit(c)) // collect integer(s), if any, after first digit found or after minus symbol found
        while (isdigit(s[++i] = c = getch()))
            ;
    if (c == '.') // collect fraction part if period is found
        while (isdigit(s[++i] = c = getch()))
            ;
    s[i] = '\0'; // terminate string after digits were captured
    if (i == 1 && s[0] == '-') // if s[0] == '-' && s[1] == '\0', return minus operator
        return '-';
    return NUMBER;
}
Personal tools