cfunctiongets

the gets_s function in C language


I wrote a test program, in this progtam there is a menu function, which allows user to choose a thing he wants to do. And there is a thing, if he chooses a task, where function gets_s is located and when, for example, user wants a task 2, he writes 2 and then presses "enter" and then the function gets_s thinks that this "enter" is for it and then my program finishes.

#include <stdio.h> 
 
void test() 
{ 
    char str[25]; 
    gets_s(str, 24); 
    printf("%s\n", str); 
} 
int menu() 
{ 
    int choice; 
    printf("Choose a task\n1 - task 1\n else...\n"); 
    scanf_s("%d", &choice); 
    return choice; 
} 
int main(void) 
{ 
    int x; 
    x = menu(); 
    if (x == 1) 
    test(); 
}

You can try this program and see that after your input 1, the program will finish. I don't understand why this happens, how can i do it without finishing the program?


Solution

  • User input is significantly more difficult than people generally to think it is (or ought to be).

    I personally find that a little helper function works, such as one to get an integer from the user. Here is one such way:

    int ask_integer( const char * prompt )
    {
      // Prompt the user for input
      if (prompt)
        printf( "%s", prompt );
    
      // Get user’s answer as a string ending with ENTER key press
      char s[100];
      if (!fgets( s, sizeof(s), stdin ))
        complain_and_quit();
    
      char * first = skipws( s );           // (skip over any leading whitespace)
      if (!*first) complain_and_quit();     // (nothing but whitespace == error)
    
      char * last = strchr( first, '\n' );  // (find the newline/ENTER)
      if (!last) complain_and_quit();       // (not found == line too long == error)
      *last = '\0';                         // (strip the newline)
    
      // Convert user’s input to an integer, if possible
      errno = 0;
      long n = strtol( first, &last, 10 );
      if (errno or (n < INT_MIN) or (n > INT_MAX))
        complain_and_quit();                // (out of range == error)
    
      last = skipws( last );                // (skip over any trailing whitespace)
      if (*last) complain_and_quit();       // (fail if input not JUST an integer)
    
      return n;
    }
    

    With

    char * skipws( char * s )
    {
      while (isspace( (unsigned char)(*s) )) ++s;
      return s;
    }
    

    Notice that there are a lot of things to check just to get an integer. Yes, it is obnoxious.

    The method you use to handle errors is up to you. For most schoolwork assignments, any variation of a simple “complain and quit” is sufficient.

    Notice the pattern:

    1. Obtain an entire line of input
    2. Attempt to validate/convert/process that input

    The using code is now much simpler:

    int menu(void)
    {
      return ask_integer(
        "Menu:\n"
        "  1 - Quux\n"
        "  2 - Frob\n"
        "  ...\n"
        "? " );
    }
    
    int main(void)
    {
      switch (menu())
      {
        case 1: test(); break;
        ...
      }
      return 0;
    }
    

    Or, using if:

    int main(void)
    {
      int choice = menu();
      if (choice == 1)
        test();
      else if (...)
        ...;
      else
        ...;
      return 0;
    }