Home > Software engineering >  Does scanf need to be checked for number of conversions if using a default switch statement?
Does scanf need to be checked for number of conversions if using a default switch statement?

Time:04-05

I have a scanf function that takes an integer 1,2, or 0. My default statement will return to a loop if there is not an input of these integers. If a character is entered, the default case still works, displays an error and returns to main. My question is if I should be using another while loop to check the scanf function for an integer, or if it is okay to keep the default statement which will return for any invalid input.

int main(int argc, char* argv[]) {
    int value;
    value = length_orWeight();
    while (value != 0) {
        value = length_orWeight();
    }
    return 0;
}

int length_orWeight(void) {
    int choice;
    printf("\nWhat would you like to convert?\n0.End Program 1.Lengths 2.Weights: ");
    scanf("%d", &choice);
    clear_keyboard_buffer();
    switch (choice) {
        case 1:
            convert_lengths();
            return 1;
        case 2:
            convert_weights();
            return 2;
        case 0:
            return 0;
        default:
            printf("\nError: You must enter 0, 1, or 2.\n");
            return 3;
    }
}

CodePudding user response:

Not checking the return value of scanf is inadequate.

The C standard does not specify what happens to objects that are not assigned values because input terminated early (as when a non-matching character was found, an end-of-file was encountered, or an input error occurred). A common behavior may be for scanf to leave the object (choice) unchanged. If this occurs in your first execution of scanf, it will remain uninitialized. However, as the standard does not define what scanf does in this regard, other behaviors are possible. For example, a rudimentary scanf implementation could, upon starting to work on %d, initialize choice to zero, in anticipation of building a number in it digit-by-digit. When it then reads a character and finds some non-digit the user entered, it could terminate with a return value indicating no conversion was performed, with choice still set to zero.

If scanf does not return a value that indicates the a conversion was completed that assigned a value to an object you passed it, you do not know what value is in that object, even if you initialized the object prior to calling scanf. Therefore, before using the object at all, you must check the return value of scanf.

CodePudding user response:

Eric Postpischil clearly stated that checking scanf's return value is necessary, but didn't point to the fact that it is a very bad function for reading input from the user. Better use fgets and sscanf:

// Reads one line
char *scanline(char *line, size_t size, FILE *istream)
{
    if (!fgets(line, size, istream)) // Input failed
        return NULL;
    
    // Replace newline with null-terminator
    size_t nl = strcspn(line, "\n");
    line[nl] = '\0';
    
    return line;
}

// Reads an int
bool scanint(int *i, FILE* istream)
{
    char buffer[255];
    if (!scanline(buffer, sizeof buffer, istream))
        return false;
    
    if (sscanf(buffer, "%d", i) != 1)
        return false;
    
    return true;
}

Also, make sure that clear_keyboard_buffer() is not fflush(stdin):

void clear_keyboard_buffer(void)
{
    scanf("%*[^\n]"); // Read and discard everything until hitting a newline.
}

Here is how your code would look like:

int length_orWeight(void)
{
    int choice;
    printf("\nWhat would you like to convert?\n0.End Program 1.Lengths 2.Weights: ");
    
    while (!scanint(&choice, stdin)) {
        printf("Please enter a valid number: ");
    };
    
    // clear_keyboard_buffer(); // No longer necessary
    switch (choice) {
        case 1:
            //convert_lengths();
            return 1;
        case 2:
            //convert_weights();
            return 2;
        case 0:
            return 0;
        default:
            printf("\nError: You must enter 0, 1, or 2.\n");
            return 3;
    }
}

int main(int argc, char* argv[])
{
    int value;
    value = length_orWeight();
    while (value != 0) {
        value = length_orWeight();
    }
}
  • Related