I am a beginner in C programming and I'm trying to do basic stuff. I'm trying to create a simple menu using a switch statement like this:
int disp(){
int check;
while(check!=1){
int choose;
printf("1 \n");
printf("5quit\n");
scanf("%d", &choose);
switch(choose)
{
case 1:func1();
break;
case 5: check = 1;
break;
default:
printf("wrong input");
}
return 0;
}
}
However, when either default or case 1 happen, I want to make it still loop the menu until a good input is made. For example, if I enter random stuff like "asdf", the program should say "wrong input" and then display the menu again and wait for user input. And if user enters "1", the program will run func1 and then go back to the menu and wait for the user to input a choice.
Currently, when a wrong input happens it just shuts down the program instead of displaying the menu again, and I don't know how to solve that.
Your return 0 statement is inside of your while loop. Move it down one line, outside of the loop.
This is one reason why proper indentation is important.
At this point, entering in non-numeric input results in an infinite loop because scanf isn't reading in anything. To remedy this, you call getchar in a loop after calling scanf to flush the buffer:
int c;
scanf(" %d", &choose);
while ((c=getchar()) != EOF && c != '\n');
This is correct and close to what you want
// Use a proper formatting standard to improve readability
int disp() {
int check = 0; // Initialize the loop variable
while (check != 1) {
int choose;
printf("1 \n");
printf("5quit\n");
scanf("%d\n", &choose); // Ask the scanf to swallow the newline
switch(choose) {
case 1:
func1();
break;
case 5:
check = 1;
break;
default:
printf("wrong input");
}
}
return 0; // Pull the statement out of the loop
}
EDIT: Since you are providing invalid input intentionally, it's better to check the return value of scanf to avoid errors.
Related
I need switch case in while. Every case in the switch is part of the menu which does something. Also I need to handle all the inputs from switch, so default isn't good choice for me. I need to leave while completely when user press CTRL + Z in CMD, where program is happening in order to free memory and quit program. This code is only simplified version of mine. Could you please help me? It's the last thing I need to do to have completed task. Thank you.
while (1) {
scanf("%c", &choose);
switch (choose) {
case 1:
//something
break;
case 2:
// something
break;
// no default!
}
}
If the user presses Ctrl+Z on Windows, your program will see that as an end of input file (stdin). When scanf tries to read a byte from stdin, it will fail and return -1 (EOF), while the usual return value is 1 (i.e. number of items read).
So you can use the following code:
while (scanf("%c", &choose) == 1)
{
switch (choose) {
case 1:
//something
break;
case 2:
// something
break;
// no default!
}
}
I put the return value of scanf into the while's condition. When scanf cannot read a byte from stdin anymore, the loop will terminate.
Your problem in handling control-Z is not leaving the switch statement, it is leaving the while statement.
On a Windows system, when the user presses control-Z at the start of an input line, the software will act similarly to an end-of-file. Notably, when scanf("%c", &choose); executes, there will be no input for it to read, so nothing will be stored in choose. Instead, scanf will return EOF to indicate no match to "%c"could be made (because no input was available). However, your code does not test the return value of scanf, so it will not see this. One way to fix this is:
while (1)
{
int n = scanf("%c", choose);
if (n != 1) // Did scanf match %c and store something in choose?
break; // No, so break out of the while loop.
switch (choose)
…
}
Then there is the matter of what should be in the switch statement. You say you “need to handle all the inputs from switch.” A char typically has 256 possible values. So, unless you have 256 case labels in the switch, you are not handling all the inputs in the switch statement. You should have a default label.
If any of the cases inside the switch needs to cause execution to leave the while statement, you can arrange this in various ways. One way is to use a variable to indicate that:
while (1)
{
int n = scanf("%c", choose);
if (n != 1) // Did scanf match %c and store something in choose?
break; // No, so break out of the while loop.
int LeaveWhile = 0;
switch (choose)
{
case 'A':
// Code to handle A input.
break;
case 'B':
…
case something: // Some case where we want to end the loop.
LeaveWhile = 1; // Indicate end of loop.
break;
…
default:
// Code to handle remaining values.
break;
}
// If it is time to leave the while loop, break out of it.
if (LeaveWhile)
break;
}
Before describing how I would handle the problem of the "break" keyword being overused in C, there is another issue you may hit that should be mentioned.
The behavior of control-C and control-Z vary with the OS and the TTY (console) settings.
Linux, with default TTY settings, will cause an interrupt with control-C, which if not handled will end the program. control-Z will suspend the program. By default, in neither case is the character delivered to the program, and so it can not be read by scanf, and it will not be dispatched by the switch statement.
Windows will also interrupt with control-C by default.
I mention this because you depend on reading control characters in your input parsing. If you aren't having the behavior you want, consider looking at TTY settings.
As for exiting the "while(1)" from inside the switch, the standard way is to change the loop from "while(1){}" to "int looping =1; while (looping){}", and set looping to 0 inside the body of the while loop.
In some cases, I will do this with (cover the eyes of the children watching) a goto to a label after the while body. I prominently display goto label, and reverse indent it to outside the level of the while loop body.
Sometimes the break condition is more natural, and sometimes the goto is clearer. If you don't have an aesthetic preference for one over the other, you might default to the break condition rather than the goto.
Add a flag at the top of the loop that gets set when you want to break out, then do so after the switch:
while (1) {
int breakout = 0;
scanf("%c", &choose);
switch (choose) {
case 1:
//something
break;
case 2:
// something else
// exit the loop in this case
breakout = 1;
break;
}
if (breakout) break;
}
you have also *do - while* loop.
bool continueLoop = true;
do
{
switch(something)
{
case smth1:
/* ... */
break
case smth2:
/* ... */
continueLoop = false; //whis will exit the loop
break;
default
/* ... */
break;
}
}while(continueLoop)
I can't seem to figure out why my program doesn't work if I call scanf() from within a case statement.
Everything works fine until I call scanf(). It's a really small console based menu. It reads a string that has just been entered and goes down the menu accordingly. It all works fine until I try scanf() in an integer that it should respond to.
I first thought that returning a value will break the Switch statement... then that didn't work so I placed break statements in just in case. But it still doesn't work though. As soon as the scanf() is not commented the whole sequence is just messed up.
void main(void) {
char MenuS;
MenuS = 's';
while (MenuS != 'q') {
MenuS = Menu();
}
}
char Menu() {
unsigned char Menu;
unsigned char UserInput[9];
unsigned int Offset;
Offset = 0;
printf("\nEnt:");
gets(UserInput);
trim(UserInput);
if (UserInput[0] != 'q') {
switch (UserInput[0]) {
case 'r':
scanf("%d", &Offset);
printf("\nIn R");
return ('r');
break;
case 'w':
// printf("Is w");
printf("\nIn W");
return ('w');
break;
default:
printf("\nSomething else");
break;
}
} else {
return 'q';
}
}
I think you're having a problem, because you declared an unsigned int variable, but you used a wrong format specifier here:
scanf("%d", &Offset);
Offset is an unsigned int variable.
You have to use %u for unsigned integer instead of %d
Try this, this might solve the problem:
scanf("%u", &Offset);
Learn to enable all warnings and debug info in your compiler. With GCC compile using gcc -Wall -g
Then use the debugger (e.g. gdb) to run your program step by step.
At last, read the documentation of functions that you are using. scanf(3) is documented to return an integer number:
These functions return the number of input items successfully matched
and assigned, which can be fewer than provided for, or even zero in
the event of an early matching failure.
The value EOF is returned if the end of input is reached before
either the first successful conversion or a matching failure occurs.
EOF is also returned if a read error occurs, in which case the error
indicator for the stream (see ferror(3)) is set, and errno is set
indicate the error.
So you should code probably
if (scanf("%d", &Offset)<1) { perror("scanf failed"); exit (EXIT_FAILURE); };
BTW, your printf(3) format strings look strange: they should end (not start) with a newline \n e.g. code printf("In R\n"); or even better
printf("line %d of %s: In R\n", __LINE__, __FILE__);
or at least call explicitly fflush(NULL); after them, read
about fflush(3) and remember that stdio(3) is buffered.
At last, gets(3) is deprecated and dangerous, use fgets(3) or preferably getline(3). So at least replace gets(UserInput); with
memset (UserInput, 0, sizeof(UserInput));
if (NULL == fgets(UserInput, sizeof(UserInput), stdin))
{ perror("fgets"); exit(EXIT_FAILURE); }
BTW, your main function is incorrectly declared. It should be int main (int argc, char**argv); preferably. It should not return void but EXIT_SUCCESS i.e. 0 on success and something else on failure. See also EXIT_SUCCESS and EXIT_FAILURE and exit(3)
PS. You are missing several #include directives at the beginning of your program, at least:
#include <stdio.h>
#include <stdlib.h>
I assume your code have them but you did not show them to us.
NB: Using capital letters in variables is not conventional in C. Generally, people
reserve upper-case letters for macros. But that is a convention.
when I call ScanF from within the Switch it seems to ignore the Return AND the Break and just to Default which just prints "Not Something"..
If it "ignored the return and the break" then you would fall through to the case 'w'. But that isn't happening. Therefore it is not ignoring the return and the break (not that that is possible anyway).
Actually the program flow is working just as you wrote it. The sequence is:
scanf("%d", &Offset);
You type some numbers and press enter
return ('r');
back to main: MenuS = Menu(); - MenuS is now 'r'
while (MenuS != 'q') { MenuS = Menu(); - call Menu again
gets(UserInput); - gets the blank string you typed earlier
if (UserInput[0] != 'q') - true, since UserInput[0] is 0, the end-of-string marker for your blank string
switch (UserInput[0]) { there is no case 0, so default: is entered
printf("\nSomething else");
To fix this: you need to discard the Enter that was pressed after you typed in the number. One way is to put this code straight after the scanf:
int c;
while ( (c = getchar()) != '\n' && c != EOF ) {}
Note, your code has a lot of other problems but this was the main issue.
I've been introduced to C this year as part of my degree, during which I have to write simple programs and test them to be idiot-proof by running them over and over again, putting nonsense variables in, etc. and I had an idea to write a program with the ability to restart itself without having to run the program again.
I've tried writing a program to perform this function (which turned out to be harder than I first thought) and I now have it working, albeit using a goto function that are frowned upon. Now the only problem I have is a while loop to check for nonsense input, that seems determined to run at least once ignoring a prompt for a valid input.
Please could someone give me an idea why this is happening? (My compiler is Dev-C++ 4.9.9.2)
int main (void)
{
mainprogram:
printf("\nPROGRAM START\n");
//code copied from an exam, to check that the program performs a function
//when ran through again
int i,j,k;
printf("Please enter 7:");
scanf("%d",&i);
printf("Please enter 4:");
scanf("%d",&j);
printf("Please enter 0:");
scanf("%d",&k);
//this is to check that the program will take input when it is restarted
do {
switch (i%j) {
case 3:
i--;
k*=i;
break;
case 2:
i--;
k+=i;
default:
i--;
k++;
break;
}
printf("i is %d k is %d\n",i,k);
} while (i>0);
//end of copied code
char prompt='y';
printf("\nRestart program?");
scanf("%c",&prompt);
while (prompt != 'y' && prompt != 'Y' && prompt != 'n' && prompt != 'N')
{
//this is the problem section, when entering nonsense input, the error messages
//display twice before pausing for input, and when restarted, the program does
//run again but displays the error messages once before pausing for input
printf("\nERROR: INVALID INPUT");
printf("\n\nRestart program?");
prompt='y';
scanf("%c",&prompt);
}
if (prompt == 'y' || prompt == 'Y')
{
goto mainprogram;
}
//
return 0;
}
while(1){ //parent
printf("\n\nRun program?");
scanf("%c",&prompt);
if (prompt == 'n' || prompt == `N`)
{
printf("\nEXITINT")
return 0;
}
int i,j,k;
printf("Please enter 7:");
scanf("%d",&i);
printf("Please enter 4:");
scanf("%d",&j);
printf("Please enter 0:");
scanf("%d",&k);
switch (i%j)
{
case 3:
i--;
k*=i;
break;
case 2:
i--;
k+=i;
break;
default:
i--;
k++;
break;
}
printf("i is %d k is %d\n",i,k);
} //end while parent
//end of copied code
There are a couple of ways one could restart a program, or more generally loop over some code. Of course, all of that can be done with gotos, but that makes the code hard to understand, so C has equivalent structures for dealing with the most common patterns:
execute code if and as long as condition holds
while (condition)
{
/* code */
}
This means before executing the code, the condition is checked. If the condition holds (its value is non-zero), the code is executed and then looped back to the top. This is equivalent to:
top_of_while:
if (!condition)
goto done;
/* code */
goto top_of_while:
done:
execute code and redo while condition holds
do
{
/* code */
} while (condition)
This means execute code first and then check for a condition. If the condition holds, executed the code again. This is equivalent to:
top_of_do_while:
/* code */
if (condition)
goto top_of_do_while;
iteration
for (initialization; condition; iteration)
{
/* code */
}
This is a kind of while loop that happens a lot, in which there is an initialization, followed by a while loop, which on the bottom changes a variable to form some sort of iteration. This is equivalent to:
initialization;
while (condition)
{
/* code */
iteration;
}
To restart a program, most likely you want the do-while loop, since for sure you know that the program has to execute once. However, by properly initialization the condition variable of a while loop, you can also ensure that the loop is always entered the first time. It's a matter of style and your liking.
Where to actually use goto
Many people would tell you to never use goto. This roots from the fact that overuse of goto had led to a great number of overly complicated programs, the so-called spaghetti code. The reason is that it's hard to build a mental model of a program where the execution can jump around to any other part.
However, gotos are actually very useful in C, without which error-handling becomes a huge pain in the ... neck. This useful usage of goto is for handling errors and cleanup, which are always in the bottom of the function. An example would be:
int *read_nums(int n)
{
int *array, i;
array = malloc(n * sizeof *array);
if (array == NULL)
goto exit_no_mem;
for (i = 0; i < n; ++i)
if (scanf("%d", &array[i]) != 1)
goto exit_bad_input;
return array;
exit_bad_input:
free(array);
exit_no_mem:
return NULL;
}
This way, the code is not cluttered (much) with error handling and cleanup is done very nicely based on how far the function has executed.
I just reformatted your code and indeed # herohuyongtao is right, the break; for case 2 has moved at the end of default which is not useful there.
But there's something really shocking in your code, is that you use a goto. Just remember that rule: WHENEVER YOU USE GOTO, THERE'S A BETTER WAY TO DO IT!
#include <stdio.h>
short int read_input (void) {
printf("\nPROGRAM START\n");
//code copied from an exam, to check that the program performs a function
//when ran through again
int i,j,k;
printf("Please enter 7:");
scanf("%d",&i);
printf("Please enter 4:");
scanf("%d",&j);
printf("Please enter 0:");
scanf("%d",&k);
//this is to check that the program will take input when it is restarted
do {
switch (i%j) {
case 3:
i--;
k*=i;
break;
case 2:
i--;
k+=i;
break; // break at the right spot
default:
i--;
k++;
}
printf("i is %d k is %d\n",i,k);
} while (i>0);
// getchar(); // could be inserted here (discards one char)
// fflush(stdin); // could also do the job (discards all remaining chars in buffer)
char prompt='y';
// here the design choice is to let the user input whatever
// and not updated the output until the right character is given
// which will hide how many wrong chars has been previously in the buffer.
printf("\nRestart program? ");
do {
prompt = getchar();
} while (prompt != 'y' && prompt != 'Y' && prompt != 'n' && prompt != 'N');
if (prompt == 'y' || prompt == 'Y')
return 1;
return 0;
}
int main() {
while (read_input() == 1);
return 0;
}
Now that the code is clean, for your exact problem, you will get into a carriage return problem. Basically, what you do is get input until \n is hit, but when you hit carriage return, you actually send CR + LF. So there's one character that never gets read in the input buffer, that you need to discard.
So you should first read that SO question that sums up very well your problem, and you can either:
add a lone getchar(); just after your //end of copied code comment,
or a fflush(stdin) can do the job (cf the SO Question to learn more about it) but has been designed about flushing output buffers, not input ones,
add a fseek(stdin,0,SEEK_END); which is a bit dirty (and non-portable) but works,
or change your conditions and your use of scanf to take into account that you're actually having more chars in the buffer.
In the code I gave you, I chose the most simplistic solution, which is to discard any wrong input without printing anything.
I have a simple program, intended simply to excercise my freshly gained knowledge of C.
Please keep in mind I have attempted to solve the problem of the newline leftovers remaining in the stdin buffer using the knowledge I already have. From the problems I was having before I figured out the newline issue, I have made considerable progress in getting this program to act the way I imagine it should. This is all I have left to debug and I am stymied. While my fluency in C is marginal, at best, I believe this program can easily work without a whole lot of modification.
The program asks the user to select a tool, of 2 simple tools I have constructed. The tools themselves work fine, but the cheesy 'pick-a-letter' UI that I've built has a bug I just can't figure out. It runs through the program as expected the first time, but the second time, the variable used for tool selection gets switched to a newline character, and the user input assignment for that variable gets overwritten. Therefore the program enters the switch construct used for the tool selection variable and activates:
case '\n':
which prints an error message indicating a newline got in the way, and breaks out to return to the beginning of the menu loop. On this third time around, the program will again work as expected. It will continue to alternate like this until you exit the program via the exit provided at the end of a normal program run.
This is a direct cut and paste of pertainant code, not included are the D_Time(); and cal(); functions. Feel free to compile and run with substitue functions to see the problem firsthand if need be. Both are void functions, one is a simple 2 argument calculator and the other displays date and time via stdout. And any other suggestions you have that could improve my knowledge of C or this program would be happily accepted and are sought. I am still learning, and although pointers and calculus escape me, i will get the hang of this.
void clrnl(void)
{
while (getc(stdin) != '\n');
} /* This is the fix that kind-of but not-really fixes the '\n' problem. Without it, the error message would pop up every time i used the menu, not including the first run. */
void menu(void)
{
char tool = 1;
int r = 0;
while (r != 1)
{
printf("\n ---Select a tool.---\n\n ------------------\n\n C = Calculator\n\n D = Date/Time\n\n ");
tool = getc(stdin);
clrnl(); /* This seems to be the only line where clrnl(); has a positive effect. */
switch (tool)
{
case 'D':
printf("\n You selected Date/Time ");
D_Time();
r = 1;
break;
case 'C':
printf("\n You selected Calculator ");
cal();
r = 1;
break;
case 'd':
printf("\n You selected Date/Time ");
D_Time();
r = 1;
break;
case 'c':
printf("\n You selected Calculator ");
cal();
r = 1;
break;
case '\n':
printf("\n Error!! -- Newline character detected!! Try again.\n\n");
r = 0;
break;
default:
printf("\n Error!! -- Unknown. Try again.\n\n");
r = 0;
break;
}
}
}
int main()
{
char lx;
do
{
menu();
printf("\n Enter 'x' to exit.\n\n ");
scanf(" %c", &lx);
if (lx != 'x')
{
continue;
}
}
while (lx != 'x');
return 0;
}
Usually a context menu is not waiting for the user to press enter before getting the option while your loop does it.
In order to select the option when pressing the relevant key instead waiting for the "enter" you can call OS specific functions. Under linux reconfigure your terminal using system ("/bin/stty raw") just before entering in your while loop and than leave things as they are. Under windows consider _getch() instead of getc(stdin).
Remember that getchar() and getc() return an integer and not a char.
Doing this you have a more reactive menu and you don't need to handle the extra chars generated.
The problem is when you input your selection, for example D, and you press enter key, various things can happen. If you're on windows, your application will recieve characters: 'D', '\r', '\n'. On linux, it will be 'D','\n'. To eat up any extra input read by scanf use this simple trick:
scanf(" %c*", &lx);
The * in format string will skip your carriage return and newline characters.
This is changed code for tool selection:
printf("\n ---Select a tool.---\n\n ------------------\n\n C = Calculator\n\n D = Date/Time\n\n ");
scanf(" %c*", &tool);
This is the part for the exit input:
printf("\n Enter 'x' to exit.\n\n ");
scanf(" %c*", &lx);
You can further put more switch cases together like this to reduce code duplication:
case 'D':
case 'd':
printf("\n You selected Date/Time ");
D_Time();
r = 1;
break;
This will work, because there's no break statement after case 'D', so if this case happens, the code continues pass the case 'd': line into the corresponding code.
I'm just starting to study C. I have a program that prints a menu and let users choose what to do step by step. Now I would like to return to the main menu whenever the user enters an empty line, but how can I do that?
I think I can make a function that return the program to the main menu, but when to call that function? I know it's not good to put an if-else whenever I scanf something...
I am used to the OO world, so this is a bit unfamiliar to me, please help :)
I'm guessing you're using a switch statement to filter your input, so just make an option for '\n', or the newline character, to output the print the menu.
If you're doing something along the lines of:
printf("0) do something\n");
printf("1) do something else\n");
printf("enter) main menu\n");
...then scanf isn't really your friend.
You could do something like this:
char buf[80];
int choice;
printf(menu_text);
fgets(buf, 80, stdin);
if(strlen(buf))
{
sscanf(buf, "%d", &choice);
switch(choice)
{
case 0:
/* etc */
break;
case 1:
/* etc */
break;
}
}
else
{
go_back_to_main_menu();
}
It depends on if the user is communicating by sending a string or a single character/keypress.
If communicating by string, try starting with:
char buffer[MAX_BUF_LEN];
char* pBuffer = buffer;
scanf("%s%*c",pBuffer);
if (strlen(pBuffer) == 0)
goto_main_menu();
else
process_user_input(pBuffer);
If communicating by character/keystroke, try starting with:
int inkey = getchar();
if (inkey == '\n')
goto_main_menu();
else
process_user_input(inkey);
Using an "if/else" after you scanf something is perfectly valid. Anything sent from the user should be checked and validated before it is used anyway.
C language is not an OO world, so I'd say stick with if-else. Anyway, when creating text menus you usually end up with switch(user_choice) of if-else.
Do you need a help with scanf()?