• Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 331
  • Last Modified:

Why isn't my scanf halting the execution of code in my C program?

I wrote the program below and started debugging. When I run the code, I expect the user to be prompted to enter an input once, but after the first prompt, there is an extra and redundant prompt in the subsequent cycles. It seems like scanf is not stopping the code from executing in order to give the user a change to enter an input. Why is that? How can I make sure that the user is asked to enter a value only once per cycle?

#include<stdio.h>
#include<stdlib.h>


int men_in_bathroom=0;
int women_in_bathroom=0;
int bathroom_capacity = 10;
int men_in_queue=0;
int women_in_queue=0;
int queue_capacity = 10;
int cycle=1;
char queueContents[1];
char eventInput;



void StatusCheck(){
    printf("\nStatus of Bathroom:\nCycle: %d  Women: %d  Men: %d\n",cycle, women_in_bathroom, men_in_bathroom);
    printf("Queue (last to first): ");
    for (int i = 0; i < men_in_queue + women_in_queue; i++){
        printf("%c ", queueContents[i]);
    }
    printf("\n");
}

void QueueReorganizer(){
    
}

void WomanWantsToEnter()
{
    if (women_in_bathroom + men_in_bathroom >= bathroom_capacity){ //if the bathroom is at full capacity
        if(men_in_queue + women_in_queue < queue_capacity){ //if the queue can accomodate more people
            printf("The bathroom is full. One woman has been added to the queue.");
            for (int i = 0; i < women_in_bathroom; i++){
                if(queueContents[i] == NULL){
                queueContents[i] = 'W';
                women_in_queue++;
                break;
                }
            }
            StatusCheck();
        }
        else{ //if the queue is at full capacity
            printf("Sorry, the bathroom and the queue are both full. No one can be added at this time.");
        }
    }
    else if(men_in_bathroom != 0){ //if the bathroom can accomodate more people but there are men currently inside the bathroom
        if(men_in_queue + women_in_queue < queue_capacity){ //if the queue can accomodate more people
            printf("Men are currently inside the bathroom. One woman has been added to the queue.");
        }
        else{ //if the queue is at full capacity
            printf("Men are currently inside the bathroom, and the queue is full. Cannot accomodate any women at the moment.");
        }
    }
    else if(men_in_bathroom == 0){ //if the bathroom can accomodate more people and there are no men currently inside the bathroom
        printf("A woman has entered the bathroom");
        women_in_bathroom++;
        StatusCheck();
        cycle++;
    }
}





void ManWantsToEnter()
{
    if (women_in_bathroom + men_in_bathroom >= bathroom_capacity){ //if the bathroom is at full capacity
        if(men_in_queue + women_in_queue < queue_capacity){ //if the queue can accomodate more people
            printf("The bathroom is full. One man has been added to the queue.");
            for (int i = 0; i < women_in_bathroom; i++){
                if(queueContents[i] == NULL){
                    queueContents[i] = 'M';
                    men_in_queue++;
                    break;
                }
            }
            StatusCheck();
        }
        else{ //if the queue is at full capacity
            printf("Sorry, the bathroom and the queue are both full. No one can be added at this time.");
        }
    }
    else if(women_in_bathroom != 0){ //if the bathroom can accomodate more people but there are women currently inside the bathroom
        if(men_in_queue + women_in_queue < queue_capacity){ //if the queue can accomodate more people
            printf("Women are currently inside the bathroom. One man has been added to the queue.");
        }
        else{ //if the queue is at full capacity
            printf("Women are currently inside the bathroom, and the queue is full. Cannot accomodate any men at the moment.");
        }
    }
    else if(women_in_bathroom == 0){ //if the bathroom can accomodate more people and there are no women currently inside the bathroom
        printf("A man has entered the bathroom");
        men_in_bathroom++;
        StatusCheck();
        cycle++;
    }
}





void WomanLeaves()
{
    if(women_in_bathroom == 0 )
    {
        printf("Invalid choice. There are already no women in the bathroom.");
    }
    else{
        women_in_bathroom--;
        for (int i = 10; i > 0; i--){
            if(queueContents[i] == 'W'){
                queueContents[i] = NULL;
                break;
            }
        }
        QueueReorganizer();
        StatusCheck();
        cycle++;
    }
}

void ManLeaves()
{
    if(men_in_bathroom == 0 )
    {
        printf("Invalid choice. There are already no women in the bathroom.");
    }
    else{
        men_in_bathroom--;
        for (int i = 10; i > 0; i--){
            if(queueContents[i] == 'M'){
                queueContents[i] = NULL;
                break;
            }
        }
        QueueReorganizer();
        StatusCheck();
        cycle++;
    }
}




int main() {
    
    while (cycle <= 20) {
        printf("\nChoose one of the following events for cycle %d:\n", cycle);
        printf("A) woman wants to enter\nB) man wants to enter\nC) woman leaves\nD) man leaves\n");
        printf("Enter A,B,C or D here --> ");
        scanf("%c", &eventInput);
        
        if(eventInput == 'A'){
            WomanWantsToEnter();
        }
        else if(eventInput == 'B'){
            ManWantsToEnter();
        }
        else if(eventInput == 'C'){
            WomanLeaves();
        }
        else if(eventInput == 'D'){
            ManLeaves();
        }
        cycle++;
    }
        

    return 0;
}

Open in new window

0
shampouya
Asked:
shampouya
2 Solutions
 
Mark BullockQA Engineer IIICommented:
The problem is reading the entire input line. You are reading one character. There is also the newline and/or return when you press the Enter key.
Here's how you can read one line at a time.
	int LINE_SIZE = 3;
        char line[LINE_SIZE];
        while (cycle <= 20) {
            printf("\nChoose one of the following events for cycle %d:\n", cycle);
            printf("A) woman wants to enter\nB) man wants to enter\nC) woman leaves\nD) man leaves\n");
            printf("Enter A,B,C or D here --> ");
            fgets(line, LINE_SIZE, stdin);
	    eventInput = line[0];

Open in new window

0
 
jmcgOwnerCommented:
The scanf function presents some problems for user input, even when you have such a promiscuous format string as "%c". A better pattern is to read a line from the console with fgets (or getline)into a buffer whose size you control. Because the fgets routine allows you to specify the maximum length of data to be read, you can avoid buffer overrun. You're not a malicious or careless user but this is a lesson to learn early.

Once that line is in your buffer, you can test -- in your case -- the first character to see if it is one of the ones you are prepared to deal with.

What I suspect is causing the "extra" prompts in your program is the fact that the end-of-line character is also something that could be returned for %c.
0
 
evilrixSenior Software Engineer (Avast)Commented:
The problem is that %c only reads the first char, but because you need to hit the return key to get scanf to process the line you also end up with line feed characters in the buffer, which are subsequently process. Expert jmcg has alluded to this already.

You can (sort of) fix this by placing the following line after your scanf:

fseek(stdin, 0L, SEEK_END);

Open in new window


This basically seeks forward through the stdin file stream until it hits EOF, which is the same as saying that it has skipped through all the unwanted characters. It's a bit of a work around rather than a solution because the problem here is that scanf is just a wholly unsafe function to use. In fact the C programming language doesn't provide any nice standard way of getting clean user input and you nearly always have to resort to using platform specific functions to do things properly.

Meanwhile, I note you are using lots of if/then/else constructs for your input. You realise you can "switch" on a char, right?

switch (eventInput)
{
   case 'A':
      // do stuff for case A
      break;

   case 'B':
      // do stuff for case B
      break;

   case 'C':
      // do stuff for case C
      break;

   case 'D':
      // do stuff for case D
      break;

   default:
      // do stuff for default case (error/quit?)
      break;
}

Open in new window

0
Has Powershell sent you back into the Stone Age?

If managing Active Directory using Windows Powershell® is making you feel like you stepped back in time, you are not alone.  For nearly 20 years, AD admins around the world have used one tool for day-to-day AD management: Hyena. Discover why.

 
sarabandeCommented:
Why isn't my scanf halting the execution of code
you may consider to using the following code instead of scanf call

       
while ((eventInput = getch()) != -1)
        {
            if (!strchr("ABCDQabcdq", eventInput))
            {
                continue;
            }
            eventInput = toupper(eventInput);
            printf("%c\n", eventInput);
            break;
        }

Open in new window


the getch gets one character from stdin but no echo. that allows to ignore invalid input and also to convert to upper case.
(note, i added also Q and q to the valid characters what allows the user to quit).

i added an else branch to  handle the 'Q':

else if (eventInput == 'D') {
       ...
}
else {
       break;   // break from while loop
}

Open in new window


note, the 'break' wouldn't work if you have changed to switch-case control as evilrix suggested since the break would end the case but not the loop. you would need to break the loop by making the loop condition wrong, for example by setting the 'cycle' counter to maximum.

case 'Q':
       cycle = 99; 
       break;
}

Open in new window


if the getch was not declared yet, you may include conio.h and ctype.h. the latter also provides a prototype of the toupper function. strchr is used to check whether the input is valid and is available thru <string.h>.

for windows platform you would either use _getch instead of getch or add

#ifdef WIN32
#define getch _getch
#endif

Open in new window


somewhere above main function.

one additional remark: your program currently compiles with c++ compiler but not with an ansi c compiler. that is due to for statements like 'for (int i = 0; ....' where you define variable i in the middle of a code block. in ansi c all variables must be defined at top of a block.

Sara
0
 
sarabandeCommented:
i dectected a few minor bugs:

if you test your code you will see that cycle was incremented twice for most cases. remove the cycle++ in the main function or (alternatively) in all the handler functions.

some printouts where stating woman where man is required.

Sara
0
 
evilrixSenior Software Engineer (Avast)Commented:
Just note that the getch function isn't part of the C standard.
0
Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.

Join & Write a Comment

Featured Post

Making Bulk Changes to Active Directory

Watch this video to see how easy it is to make mass changes to Active Directory from an external text file without using complicated scripts.

Tackle projects and never again get stuck behind a technical roadblock.
Join Now