Carlos Luis Carlos Luis - 3 months ago 18
C Question

Boolean expressions on C

Creating a simple C function that prevents the user from entering anything other than a number between 1 and 9. Any other input including letters symbols and any number that is either less than 1 and greater than 9 should not be accepted.
So far its pretty straight forward; however, the part where the code is supposed to check that the entered character is not a symbol or letter is not working the way I wanted it to work.

int validateUserInput(){
printf("%s\n", "Please enter a number from 1 to 9: ");

char value = getchar();
int numValue = value;
char temp;

int digitCounter = 0;

while((temp = getchar()) != '\n'){
digitCounter++;
}
//if there is more than 1 digit.
if(digitCounter>0){
printf("%s\n","Input too long!");
return validateUserInput();
}
// if the char entered is not between 1 and 9
// this part is giving me a hard time.
else if(numValue < 49 || numValue > 57){
printf("%s\n", "Imput is not within the valid parameters");
return validateUserInput();
}
return numValue;
}

Answer

There are a couple of additional points to look at when taking input with your input routine. What if the user needs to cancel input? (e.g. presses ctrl+d, or ctrl+z on windows) As you have it written, there is no way to cancel the loop. (while that works for your purpose of forcing only a single input of 1-9, it leaves no way to cancel) If you trap EOF, you provide both, a way to cancel, and a way to indicate cancellation back in the calling function (e.g. by checking the return against EOF)

While recursion has its place, be aware that each recursion is itself a separate function call that requires a separate stack, and all the other trappings of a function call. There are many times when that can be avoided with a simple goto statement. glibc makes regular use of goto (e.g., check the source of qsort, getdelim, etc..) In your case, a single goto label can completely eliminate the need for recursion. For example, you could do something similar to the following while meeting all of your criteria:

int validateuserinput()
{
    int c, extra, tmp;

    getinput:;  /* a simple goto can avoid recursion completely */
    extra = 0;
    printf ("Please enter a number from 1 to 9: ");
    /* prompt/loop while chars not between 1 and 9 */
    for (c = getchar(); (c < '1' || '9' < c ); c = getchar()) {
        if (c == '\n') {    /* no digits in input */
            fprintf (stderr, "error: invalid input.\n");
            goto getinput;
        }
        else if (c == EOF) {  /* trap EOF */
            fprintf (stderr, "\nerror: input canceled.\n");
            return c;
        }
    }
    /* empty input buffer -- increment extra count */
    for (tmp = getchar(); tmp != '\n' && tmp != EOF; tmp = getchar())
        extra++;

    if (extra) { /* if extra chars -- input too long */
        fprintf (stderr, "error: input too long.\n");
        goto getinput;
    }

    return c - '0';  /* return integer value instead of ASCII value */
}

(just a style note, C generally avoids the use of camelCase variable names in favor of lower-case, that's just a generality, it's entirely up to you)

You can check the function (and respond to a cancellation) with the following short bit of code:

#include <stdio.h>

int validateuserinput();

int main (void) {

    int n;

    if ((n = validateuserinput()) != EOF)
        printf ("\n valid input : %d\n", n);

    return 0;
}

Example Use/Output

Testing accepting input of only 1-9:

$ ./bin/inputhandler
Please enter a number from 1 to 9: Hello World!
error: invalid input.
Please enter a number from 1 to 9: ?
error: invalid input.
Please enter a number from 1 to 9: 0
error: invalid input.
Please enter a number from 1 to 9: 23
error: input too long.
Please enter a number from 1 to 9: 6

 valid input : 6

Testing input cancellation (e.g. ctrl+d, or ctrl+z on windows)

$ ./bin/inputhandler
Please enter a number from 1 to 9:
error: input canceled.

While there is nothing wrong with using recursion, it always helps to ask "Does this need to be a recursive function to begin with?" Sometimes the answer will be yes, but often there are simple ways to avoid the additional overhead. (note: the overhead with a few recursions is minimal so it isn't a big consideration in your case, but if you unwittingly call a recursive function that spins a million times, it rapidly becomes a concern)

Look over all answers, and if you have any questions, just let us know.