[SOLVED] - improper scanf input causing infinite loop

I'm writing a display function that will prompt the user to select form a list that is displayed. The list is stored in a 2d char array so the index can be used in the rest of the program. The function should just return an integer representing the appropriate choice or -1 for error. My problem is if the input is not an integer, ie the user accidently enters a character of some kind, then the functions starts into an infinite loop. Not sure how to best make this work. I should mention I'm using DevC++4.9.9.2 on a Windows PC, and if you couldn't guess, I'm coding in c, which should explain all the printf's and scanf's.

My code for function looks like this...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
//this is part of the Functions.c file
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
#include <time.h>
#include <ctype.h>
//function prototypes and global constants
#include "StcWx2_Version2.Header.h"
#define HEADER_MAX_WIDTH 55

int ChooseSearchType(  char arg[][256], int length  ){
   //Prompt user for search type
   int i, test=0, choice = 0;
   DisplayHeader( "Select type of search to perform", HEADER_MAX_WIDTH, "*" );
   //loop and display each option
   for( i=0; i<length; i++ ){
      printf( "%4d....%s.\n", i, arg[i] );
   }
   printf( "\nOption: " );
   //scan in the index of appropriate choice
   scanf( "%1d", &test );
   printf( "\n" );
   system( "PAUSE" );	
   //check for errors
   if( test != 0 || test != 1  || test != 2 || test != 3 
       || test != 4  || test != 5  || test != 6 ){  
      return -1;
   } else {
      choice = test;
      return choice;
   }
}
....many more user defined functions


Then the main function looks like this...

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
//Main.c with extra stuff removed to save space
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
#include <time.h>
#include <ctype.h>
//function prototypes and global constants
#include "StcWx2_Version2.Header.h"
#define DATAFILE_COUNT 4
#define DATASOURCE_OPTIONS_COUNT 7
#define SEARCH_OPTIONS_COUNT 7
#define HEADER_MAX_WIDTH 55

int main(void)
{
   clock_t start,end;
   start = clock();
   unsigned short int
      *year, *month, *day;
   short int
      *high, *low;
   int
      i=0, k=0, records = 0, datasource, search_type, 
      search_match_index[1000], datasources_used, dummy;
   char
      f[FILENAME_MAX],
      selected_filename[FILENAME_MAX],
      datasource_options[DATASOURCE_OPTIONS_COUNT][FILENAME_MAX] = { 
         "StcWx00to29.txt", "StcWx30to59.txt", "StcWx60to90.txt", 
         "StcWx91to08.txt", "TestDataSet.txt", "All files", 
         "Enter a file name" },
      search_options[SEARCH_OPTIONS_COUNT][256] = {
         "Record highs and lows",
         "Number of days in a selected year with no precipitation",
         "Longest dry period",
         "Most snowy days in a season",
         "Record highs and lows on your birthdays",
         "Record highs and lows since your birthday", 
         "Quit Program" };
   float
      *precip;
   double running_time;

   //Some pointers
   FILE* fptr[DATAFILE_COUNT];
 
   //switch statements for search options, 
   //wrapped in do while for error checking
   do{
      //user prompts
      search_type = ChooseSearchType( search_options, SEARCH_OPTIONS_COUNT );
      switch( search_type ){
         case 0:
            .....
         case 6:
            exit(0);
         //no default search_type only returns 0-6
      }
   }while( search_type == -1 );

//.....more code, then ends with...

   //some timing calculations
   end = clock();
   running_time = (double)(end - start) / (double)CLOCKS_PER_SEC;
   printf ("\n\nExecution time: %.4lf seconds\n", running_time );
   system( "PAUSE" );	
   return 0;
}


The output looks a little something like this...


********** Select type of search to perform **********

0....Record highs and lows.
1....Number of days in a selected year with no precipitation.
2....Longest dry period.
3....Most snowy days in a season.
4....Record highs and lows on your birthdays.
5....Record highs and lows since your birthday.
6....Quit Program.

Option: p

Press any key to continue . . .
********** Select type of search to perform **********

0....Record highs and lows.
1....Number of days in a selected year with no precipitation.
2....Longest dry period.
3....Most snowy days in a season.
4....Record highs and lows on your birthdays.
5....Record highs and lows since your birthday.
6....Quit Program.

Option:
Press any key to continue . . .[cursur ends up here]

Notice i typed p instead of 0, the second loop comes up but skips past the options input and stops at the system("PAUSE").

My goal was a function that only returns the values -1 to 6 (maybe more if i add data files to be searched.) But only integers, because they will be used later to create/perform other tasks. If the function returned -1 then call the function again and get the user to type correct response. The system("PAUSE")'s have been added to see where things are going wrong. I've tried all sorts of if else statements, adding variables for checking, I just can't get it figured.

If you could suggest how to gracefully handle a user input that is not an integer value. I would be more than appreciative. Thanks in advance.
Last edited on
Well as far as scanf() goes...I don't think there is a way to do that...you could have the user input a character and then just check the char for '1' '2' etc...
do you mean like this...

scanf( "%c", &test );

then...

 
if( test == '1' ...){}


I've been toying with the idea of scanning in as a string, converting, and then checking. would scanning in a char work better? i was just confused as to why the scanf() call isn't working on each successive loop. is there a variable or reference that is carrying over between calls to the function? or is there an error that i could/need to clear somehow? it was my understanding that all variables and data used in a function go away after the function executes and returns unless a static variable is set. Any idea's? Thanks for the help though firedraco.
Yeah, that's what I was thinking of. Although since you are using a char now, you might have excess data sitting in the buffer, which is probably the problem you are getting...Idr exactly how scanf() works with that exactly though, you might want to look that up.
that is my biggest mystery with the scanf() function, what happens with errors and how it handles the buffer stuff. thanks again. hope some one else can give me a clue or a link! Will be trying the char/string idea soon...will post if i get it figured.
If scanf() cannot match the input with the format, scanning stops at the first character in the input which cannot be matched and returns immediately. The number of arguments matched is returned. The "file pointer" on the input stream is left at the character that did not match.

ie,

input stream = "12345 abcdef"

int i1, i2;
scanf( "%d %d", &i1, &i2 );

i1 is filled in with 12345.
i2 is left uninitialized.

scanf() returns 1.

next scanf, the "a" will be the first character seen.
Thank you, that helps explain the infinite loop that happens. I will work on devising a better way to handle the possibilities of what a user could type. did i mention i don't like scanf!

so your comment jsmith gave me an idea. i re-wrote the if else at the bottom of the original function definition like so...

1
2
3
4
5
6
if( /* bad input*/ ){
   fflush (stdin);
   return -1;
} else {
   return choice;
}


and so far so good... Is this bad style? Code? or is there any other issues or information you could provide about using the flush statement in this way. I've read a few things about it and it seams to work in this example (ie for a homework assignment that doesn't need to be "portable", or used in an advanced program, or with input from anything other than the keyboard and data files). From what i read in this section...

http://www.cplusplus.com/reference/clibrary/cstdio/fflush.html

i noticed this comment

...If the argument is a null pointer, all open files are flushed...


would stdin ever be a null pointer?

Thanks again and kudos to all the guys who help on this site, it has provided me with a wealth of knowledge that the instructor for my class has not been able to match!!!
I would say that that is fine.

I would think that stdin would never be null unless you assigned it null directly.
Cool then this thread can be tagged as solved!!!
You can use the "edit" button on your first post to edit the subject I believe.
Topic archived. No new replies allowed.