Problems with restarting program - c

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.

Related

Make default statement not exit (C)

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.

Why would scanf make my switch statement freak out?

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.

Loop statement problems

At the end of my program I want to ask the user if he wants to exit or not.
PROBLEM: I want to create a loop, that would send the user to beginning (Point A) if he enters n or N, or
to the end of my program (Point C) if he enters y or Y, or to the middle of the program (Point B) if he enters anything else.
How do I achieve this?
#include <stdio.h>
int main()
{
char exit=n;
A POINT
// imagine here some stuff
// some more stuff
// just a bit more junk
// some other C statements
B POINT
printf("wana exit? press y/n);
scanf("%c", &exit);
C POINT
return 0;
}
This is pretty basic. What you're really asking is how to control a main loop based on a valid response, and keep asking the question if the response is not valid. One way to do this is using the value as your loop condition.
do {
// etc etc...
// Ask question until yes or no answer
do {
printf("Wanna exit? Press y/n\n");
if( 1 != scanf("%c", &exit) ) {
printf("Input failed. Exiting.\n");
exit = 'y';
}
exit = tolower(exit);
} while(exit != 'y' && exit != 'n');
} while(exit != 'y');
Other approaches use loop control constructs such as break and continue. There's already an answer here using those.
You can wrap the whole code in main in a do { ... } while (false), and use continue; to return to the top, and break; to exit the loop, e.g.:
main() {
do {
// your code
if (exit == 'n')
continue;
} while (false);
}
PS: You could use goto, but that's highly discouraged, so better don't even start to use it.

Input Function Skip. Applied fix. Odd result

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.

Program ends with exit code 0 immediately - but it shouldn't get there without input

Build succeeds. If I open the output window, it reads:
Program ended with exit code: 0
But my program is such that this shouldn't be possible, without first having taken user input, done some stuff, and taken another user input, all in int main().
The first thing int main() does is loop through taking input for p, until the input is one (of two) desired options. So there's no way it should be able to exit immediately - it initialises p=0 and doesn't exit a while loop until p is 1 or 2.
Is there some hidden error that has allowed the build to succeed without it actually.. succeeding?
int main(){
//vars
while (TRUE){
//play computer or human?
while (!(p == 1 || p == 2)) {
printf("Single player or two player? (1/2): ");
scanf("%d", &p);
}
if (p==1) {
//play computer
}
else {
//snip
}
printf("%s won the game! Play again?", winner);
scanf("%s", playagain);
if (strncmp(playagain,"no",2)==0){
break;
}
}
return 0;
}
Print out what uppercase TRUE is defined as. I have seen some ambitious but inexperienced folks do weird things with it. You might not even be getting into your main while loop.
printf("TRUE is %d\n",TRUE);
If this is nonzero, then your problem is elsewhere.

Resources