C - K&R getint() variation I'm currently learning C with "The C Programming Language" by K&amp;R. At the moment I'm at the chapter about pointers (since I come from Java, they are new to me). In the book is an example code for a method getint(), which is supposed to read integers from input, but doesn't handle freestanding '+' and '-' correctly.


  As written, getint treats a + or - not followed by a digit as a valid representation of zero. Fix it to push such a character back on the input. 


This is what I wrote:

int getint(int *pn)
{
   int c, sign;

   while(isspace(c = getch()))
      ;

   if(c == EOF)
      return EOF;

   if(!isdigit(c) &amp;&amp; c != '+' &amp;&amp; c != '-')
      return 0;

   sign = (c == '-') ? -1 : 1;

   if(c == '+' || c == '-')
      c = getch();

   if(!isdigit(c)) {
      ungetch(c);
      return 0;
   }

   *pn = 0;
   while(isdigit(c)) {
      *pn = *pn * 10 + c - '0';
      c = getch();
   }

   *pn *= sign;
   ungetch(c);

   return 1;
}


I modified the version inside K&amp;R quite a bit and was wondering if I did a good job at doing so. My getint() is supposed to return EOF when end of file is reached, 0 if it failed to read the current character as an integer and 1 if it successfully wrote the int into *pn.

The K&amp;R code begins with the follwing lines: 

int c, sign;
while(isspace(c = getch()))
    ;

if(!isdigit(c) &amp;&amp; c != EOF &amp;&amp; c != '+' &amp;&amp; c != '-') {
    ungetch(c);
    return 0;
}


And I think that the code will stuck if a non-digit, non-'+/-' and non EOF file character is found. Since it'll just unget this char and read it again in the next function call. Is this made on purpose? I left out the ungetch(), since I think that the function is supposed to just read the next char of input in the next function call...
OP's dropping of ungetc() is incorrect.  OP's thought of "it'll just unget this char and read it again in the next function call." assumes the next function call will be a repeated call to getint() - this is not warrented.  Since the calling code should inspect the return value and noting it is not 1, execute other code to consume the data in stdin.  

if(!isdigit(c) &amp;&amp; c != '+' &amp;&amp; c != '-') {
  ungetch(c);  // not present in OP code, but present in K&amp;R
  return 0;
}


The though being, if getint(int *pn) cannot read the int, leave stdin alone as much as practicable.  Other functionality could be to at least consume 1 character, but I find the first more in keeping with C I/O functions.



A quandary:

In other cases, OP's code below does "Fix it to push such a character back on the input" but IMO does not push back properly.  The sign character is not push-backed, only the digit.  Instead the sign is consumed by getint().

    c = getch()
...
sign = (c == '-') ? -1 : 1;
if(c == '+' || c == '-')
  c = getch();
if(!isdigit(c)) {
  ungetch(c);  // non-digit push-backed, but not sign.
  return 0;
}


But portably using ungetc() twice in a row is not guaranteed.


  One character of pushback is guaranteed. If the ungetc function is called too many times on the same stream without an intervening read or file positioning operation on that stream, the operation may fail.  C11dr §7.21.7.10 3


Since code really should "unget" 2 characters (+/- and the following non-digit), but it is not portable - what to do?  Since many systems do support pushing back more than 1 character with restrictions, consider attempting it.  

Of course, all the consumed white-space characters are not push-backed either.  Yet pushing back the sign is prudent - many systems do this with scanf("%d", ...).

Checking the return value of ungetc() would discern success.  A slight variation follows:

int c;
int sign = EOF;

while(isspace(c = getch()))
  ;
if(c == EOF)
  return EOF;
sign = c;
if (sign == '+' || sign == '-') 
  c = getch();

if (!isdigit(c)) {
  ungetch(c);  // Pedantic code would check this return value.
  if (sign == '+' || sign == '-') {
    // attempt to push-back sign
    if (ungetch(sign) == EOF) {
      ; // Operation failed
    }
  }
  return 0;
}

...

if (sign == '-') *pn = -(*pn);




Other code issues:

Code does not detect overflow.

Code unnecessarily overflows when scanning INT_MIN.  parse_int(char **s) offers a candidate solution.

Code does not differentiate between an EOF received from getch() due to end-of-file and input error.  Coding goal is "return EOF when end of file is reached".  OTOH, code has no specification concerning input error, so returning EOF for both conditions is reasonable.