Forcing a Decision

Programs often ask questions: yes-or-no and multiple choice. The user must select one of the given options, which I refer to as forcing a decision. Your goal as programmer is to pester the user to input a valid choice.

The easy and common way to resolve this pickle is to select a default option and, when the user is goofy, the default is selected.

For example, Linux installation scripts offer the user a Yes or No (what I call a “yorn”) option. If the user types anything other than Y or sometimes Y-E-S, computer says, “Okay, you mean ‘no'” and proceeds. This approach doesn’t really force a decision; it presents a default.

My approach for forcing a decision is to bury a switch-case structure within an endless while loop. A specific variable is required to ensure that the loop repeats until a correct choice is made. I consistently name this variable done because it makes the code read well:

2022_07_30-Lesson.c

#include <stdio.h>

int main()
{
    const int true=1,false=0;
    int ch,done;

    /* prepare for the loop */
    done = false;    /* the loop is not done at this point */

    while(!done)    /* "while not done" because
                       !done (not done) == TRUE */
    {
        printf("You must choose A, B, or C: ");
        ch = getchar();

        /* check their results */
        switch(ch)
        {
            /* check for both upper- and lowercase */
            case 'A':
            case 'a':
                /* do whatever */
                puts("Carrying out option A");
                /* end the loop condition */
                done = true;
                break;
            case 'B':
            case 'b':
                puts("Carrying out option B");
                done = true;
                break;
            case 'C':
            case 'c':
                puts("Carrying out option C");
                done = true;
                break;
            default:
                puts("Invalid choice");
                /* empty the stdin buffer */
                while( getchar()!='\n' )
                    ;
        }
    }
    puts("Thank you for behaving");

    return(0);
}

Variable done is initially set to FALSE (zero). As the while loop’s condition is !done (“not done”), it translates to TRUE, so the loop repeats until the value of done becomes FALSE.

Within the while loop, switch-case statements filter possible correct responses. If any of the six valid choices are input, the code outputs a confirmation text message (or does something more interesting in real life) and the value of done becomes TRUE. The loop is done.

The default condition handles any invalid input. An error message is output, then an inner while loop churns through whatever text lingers in the stdin input buffer until a newline is encountered:

while( getchar()!='\n' )
    ;

This statement gobbles the newline as well, which makes stream input behave in a familiar manner for the user.

Here’s a sample run:

You must choose A, B, or C: hello
Invalid choice
You must choose A, B, or C: d
Invalid choice
You must choose A, B, or C: a
Carrying out option A
Thank you for behaving

I wrote an Exercise years back that dealt with a similar situation, though only for “yorn” responses. It seems a bit clunky now that I look in the rearview mirror. The method presented in this post is far more elegant and can easily be adapted to any number of options for forcing a decision.

5 thoughts on “Forcing a Decision

  1. Just a very minor point but with stuff like this I usually call toupper() or tolower() on the input which cuts down on a few lines of code, particularly if there are several options. (They live in ctype.h btw.)

    You could use options 0, 1, 2 etc and use them to index an array of function pointers, if you’re the sort of person who likes to be baffled by your own code.

  2. I forgot to ask whether there’s a reason you never use stdbool.h which lets you use bool, true and false. It was introduced with C99 so should be pretty universally supported by now.

  3. I like the idea of using numbers. Clever on the indexing.

    Even in languages that have ‘true’ and ‘false’ defined, I still don’t use them. I suppose it’s just habit, but you’re correct. And I believe they do become keywords with the C23 update. I’m still looking for a C23 compatible compiler to play with.

  4. I was wondering why it isn’t possible to do something like this:

    case ‘A’ || ‘a’:

    so I tried it and it compiles (with gcc 9.3.0) but doesn’t work. You still get “Invalid choice” even if you type a valid letter. Very strange.

    This is a Wikipedia article for C2x. At the top it says

    “C2x is an informal name for the next (after C17) major C language standard revision. It is expected to be voted on in 2023 and would therefore be C23”.

    At the bottom of the page it says

    “The GCC 9, Clang 9.0, and Pelles C 11.00 compilers implement a compiler flag to support this standard”.

    I assume this would be -std=c23, or maybe -std=c2x.

  5. Even if case 'A' || 'a': worked, it would probably evaluate to 0 or 1 depending on the logic.

    I’ll look into upgrading my clang. The current version lack an std=c23 or similar switch. I’m eager to check it out.

Leave a Reply