The C Programming Language, 2nd Edition, by Kernighan and Ritchie
Exercise 5.01 on page 97
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.
Solution by Gregory Pietsch
#include <ctype.h> #include <stdio.h> /* for EOF */ int getch(void); void ungetch(int); /* getint: get next integer from input into *pn * 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. */ int getint(int *pn) { int c, sign, sawsign; while (isspace(c = getch())) /* skip white space */ ; if (!isdigit(c) && c != EOF && c != '+' && c != '-') { ungetch(c); /* it's not a number */ return 0; } sign = (c == '-') ? -1 : 1; if (sawsign = (c == '+' || c == '-')) c = getch(); if (!isdigit(c)) { ungetch(c); if (sawsign) ungetch((sign == -1) ? '-' : '+'); return 0; } for (*pn = 0; isdigit(c); c = getch()) *pn = 10 * *pn + (c - '0'); *pn *= sign; if (c != EOF) ungetch(c); return c; }
Solution by Jose G. López (Category 0)
#include <stdio.h> #include <ctype.h> /* for isdigit(c), etc. */ #define MAX 10 int getint(int *pn); int main(void) { int i, num[MAX]; int val; for (i = 0; i < MAX && (val = getint(&num[i])) != EOF; i++) printf("num[%d] = %d, \tvalue returned: %d (%s)\n", i, num[i], val, val != 0 ? "number" : "not a number"); return 0; } int getch(void); void ungetch(int c); int getint(int *pn) { int c, sign; *pn = 0; while (isspace(c = getch())) ; if (!isdigit(c) && c != EOF && c != '+' && c != '-') { /* ungetch(c);*/ /* if returned to input we fill the array */ return 0; } sign = (c == '-') ? -1 : 1; if (c == '+' || c == '-') { c = getch(); if (!isdigit(c)) { ungetch(sign == 1 ? '+' : '-'); return 0; } } while (isdigit(c)) { *pn = 10 * *pn + (c - '0'); c = getch(); } *pn *= sign; if (c != EOF) ungetch(c); return c; } int bufp = 0; int buf[MAX]; int getch(void) { return bufp > 0 ? buf[--bufp] : getchar(); } void ungetch(int c) { if (bufp < MAX) buf[bufp++] = c; }
Solution by menonsahab
/* I've added a function to view the buffer elements. */ #include <ctype.h> #include <stdio.h> #define BUFFERLENGTH 100 int getch(void); void ungetch(int); int buf[BUFFERLENGTH]; int nfp = 0; void viewbuffer(void) { int i; printf("\nbuffer:\n"); for(i = nfp - 1; i >= 0; i--) printf("%d\n", buf[i]); } int getch(void) { return (nfp > 0) ? buf[--nfp] : getchar(); } void ungetch(int c) { if(nfp < BUFFERLENGTH) buf[nfp++] = c; else printf("error: ungetch buffer overflow\n"); } int getint(int *pn) { int c, sign; while(isspace(c = getch())); if(!isdigit(c) && c != EOF && c != '+' && c != '-') { ungetch(c); return 0; } sign = (c == '-') ? -1 : 1; if(c == '+' || c == '-') // If character read is + or - { c = getch(); // Read next character if(!isdigit(c)) // If it is not a digit { ungetch(sign == -1 ? '-' : '+'); // pushback on to the buffer the operator that was previously read return 0; } } for(*pn = 0; isdigit(c); c = getch()) *pn = *pn * 10 + (c - '0'); *pn *= sign; if(c != EOF) ungetch(c); return c; } int main() { int x, retval; int* px; px = &x; retval = getint(px); printf("retval = %d, x = %d\n", retval, x); viewbuffer(); return 0; }
Solution by Luke Panayi
#include <stdio.h> #include <ctype.h> #define BUFFER 1000 char buf[BUFFER]; int bufp=0; void ungetch(int c) { if (bufp >= BUFFER) printf("ungetch: too many characters\n"); else buf[bufp++] = c; } int getch(void) { return (bufp > 0) ? buf[--bufp] : getchar(); } int getint(int *pn) { int c, sign; while (isspace(c = getch())); if (!isdigit(c) && c != EOF && c != '+' && c != '-') { ungetch(c); //not a number return 0; } sign = (c == '-') ? -1 : 1; if (c == '+' || c == '-') c = getch(); for (*pn = 0; isdigit(c); c = getch()) *pn = 10 * *pn + (c - '0'); if (*pn == 0 && c != '0') { //if +/- is followed by NaN, pn will stay at 0 ungetch(sign == 1 ? '+' : '-'); return 0; } *pn *= sign; if (c != EOF) ungetch(c); return c; } int main() { int n, array[BUFFER]; for (n = 0; n < BUFFER && getint(&array[n]) != EOF; n++); printf("%d\n", array[1]); return 0; }
Solution by DtxdF
#include <stdio.h> #include <stdlib.h> #include <ctype.h> /* Used by getch/ungetch */ int buff = EOF; int getint(int *pn); int main(void) { int number; getint(&number); printf("%d\n", number); return EXIT_SUCCESS; } int getch(void) { int temp; if (buff != EOF) { temp = buff; buff = EOF; } else temp = getchar(); return temp; } void ungetch(int c) { if (buff != EOF) fputs("ungetch: Too many characters", stderr); else buff = c; } int getint(int *pn) { int c, sign; while (isspace(c = getch())) ; if (!isdigit(c) && c != EOF && c != '+' && c != '-') { ungetch(c); return 0; } sign = (c == '-') ? -1 : 1 ; /** * Workaround: When the user enters a number with more than one * sign (+/-) causing it to be set to 0, it also happens when * you enter one or more signed digits and include characters * other than numbers. * * The following examples resulted in 0 being obtained: * 1): --1: What you wanted was -1, but you got 0. * 2): ++1: What you wanted was 1, but you got 0. * 3): -a1: You wanted it was -1, but you got 0. * 4): +a1: What you wanted was 1, but you got 0. * 5): --------1: What I wanted was -1, but you got 0. * * Now the user can enter data like the above, and would get: * -1, 1, -1, 1, -1. * **/ while (!isdigit(c)) c = getch(); for (*pn = 0; isdigit(c); c = getch()) *pn = 10 * *pn + (c-'0'); *pn *= sign; if (c != EOF) ungetch(c); return c; }
Solution by anonymous
The wording of the exercise could be improved because it isn't clear what is being asked of the reader. My interpretation is if you discover a plus or minus symbol followed by a non-digit, then push the non-digit back to the buffer, and return 0 which represents a non-digit was found. Something that was implied is to make sure an EOF is returned if found instead of returning zero and processing the EOF the next go around. I also took the liberty initializing the values of the array to zero so they aren't garbage if the function returns early before setting it to zero in the for loop. Finally, I added a for loop in main to print out the values set in the array, which greatly helped in understanding what was going on (e.g. if there are no digits, the program keeps trying to set the same non-digit char over and over again until n == SIZE and the initial for loop exits). This isn't ideal and this is the result of pushing "such a character back on the input" as requested. Honestly, the "correct" way of doing this is to discard garbage and only allow valid input.
#include <stdio.h> #include <ctype.h> /* Exercise 5-1. 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. */ #define BUFSIZE 100 #define SIZE 100 char buf[BUFSIZE]; // buffer for ungetch int bufp = 0; // next free position in buf int getch(void); void ungetch(int); int getint(int *pn); int main() { int n, array[SIZE]; for (n = 0; n < SIZE && getint(&array[n]) != EOF; n++) ; for (int i = 0; i < n; i++) printf("%d\n", array[i]); return 0; } int getint(int *pn) { int c, sign; *pn = 0; // set value to zero so it isn't garbage while (isspace(c = getch())) // skip white space ; if (!isdigit(c) && c != EOF && c != '+' && c != '-') { ungetch(c); // not a number return 0; } sign = (c == '-') ? -1 : 1; if (c == '+' || c == '-') { c = getch(); if (!isdigit(c) && c != EOF) // don't ungetch EOF, we want that returned this iteration { ungetch(c); // not a number return 0; } } for (*pn = 0; isdigit(c); c = getch()) *pn = 10 * *pn + (c - '0'); *pn *= sign; if (c != EOF) ungetch(c); return c; } // get a (possibly pushed back) character int getch(void) { return (bufp > 0) ? buf[--bufp] : getchar(); } // push character back on input void ungetch(int c) { if (bufp >= BUFSIZE) printf("ungetch: too many characters\n"); else buf[bufp++] = c; }