FAQ > How do I get a number from the user (C)


Match word(s).

If you have any questions or comments,
please visit us on the Forums.

FAQ > How do I... (Level 1) > How do I get a number from the user (C)

This item was added on: 2003/01/23

Getting a number from the user may sound like a simple thing, and indeed it is. However, there are various pitfalls people fall into when trying to do this for the first time, and hopefully this FAQ will help a few of you out. The first thing you need to understand is what a number actually is, from both a user and program perspective. This will be discussed first, followed by various methods, some good and some bad, of reading that number. The sections are:

  • An Introduction to Numbers
  • Converting ASCII to Numeric
  • Formatted Input Functions
  • Read As A String and Convert
  • Custom Validation

    When you press a number on your keyboard, eg 7, you are entering the character 7, which is represented internally by another number. Assuming you're on an ASCII platform, a character 7 in the ASCII Table has a value of 55 (0x37 in hexadecimal).

    When you use functions to read strings or single characters from the keyboard, eg. fgets() and getchar(), they will store the ASCII values of the input. Therefore, it is important to remember that a 7 from the user isn't a 7 within your program. Here is an example program to show you an incorrect use of a character based number.

    
    #include <stdio.h> 
    
    /*
     * This program shows the incorrect use of input functions
     * to obtain a number from the user.
     */
    
    int main(void)
    {
      int num;
      int i;
      
      printf ("How many time do you want the loop to run? ");
      num = getchar();
      
      for (i = 0; i < num; i++)
        puts ("Looping");
      
      printf ("The loop ran %d times\n", num);
      
      return(0);
    }
    
    
    /*
     * Program output:
       How many time do you want the loop to run? 7
       Looping
       Looping
       <-- snipped out -->
       Looping
       Looping
       The loop ran 55 times
     *
     */
    
    

    There are various methods you can use to obtain the actual number. Some are easier to understand, and some are safer to use. By safer, I mean it is harder for the user to provide invalid input that will corrupt your program.

    OPTION 1 Convert ASCII to Numeric

    Following on from our previous example of poor code, it is possible to apply one line to fix the problem. If you look closely at the ASCII Table, you will see that '0' to '9' are represented as 48 to 57. A simple maths calculation will allow us to do this:

    '7' - '0' = 7

    Here we have taken a character 0 away from a character 7. In ASCII terms the same calculation is:

    55 - 48 = 7

    The result is a numeric 7. Here is the amended program and it's output:

    
    #include <stdio.h> 
    
    /*
     * A fixed version of the previous program
     */
    
    int main(void)
    {
      int num;
      int i;
      
      printf ("How many time do you want the loop to run? ");
      num = getchar();
      
      num -= '0';
      
      for (i = 0; i < num; i++)
        puts ("Looping");
      
      printf ("The loop ran %d times\n", num);
      
      return(0);
    }
    
    /*
     * Program output:
       How many time do you want the loop to run? 7
       Looping
       Looping
       Looping
       Looping
       Looping
       Looping
       Looping
       The loop ran 7 times
     *
     */
    
    

    This method has its problems, though. It only allows for single digit numbers and it will give undesirable results if the user enters anything other than 0 - 9. There are much better methods to get a number, so don't use this one!

    If you're wondering why I included example code that you shouldn't use, it's because I needed to be sure you understood what the term "number" means to the user and to the program. Now you know (hopefully!), I can move on to show you better methods.


    OPTION 2 Formatted Input Functions

    Formatted input functions perform the conversion we saw earlier, so that you don't have to. The functions are scanf(), sscanf() and fscanf(). They all perform the same basic process, with scanf() getting its input from they keyboard, sscanf() from another memory location (variable), and fscanf() from a file stream. For now, I'll concentrate on scanf(), and leave you to research the others if you want to.

    Before I go on, I'd like to say this is not a senior programmer's prefered method for getting a number. It has various faults, which I will detail later. But, it does have its place in the world, and you should know how to use it properly, so here goes...

    The prototype:
    int scanf( const char *format, ...);

    The first parameter of the scanf() function is a format list that takes the form of "xxx", where xxx are the format modifiers. For now we're only talking about numbers, so I'll limit this subject to modifier %d. There are many more, I suggest you research them.

    The second parameter, and indeed any subsequent ones, are pointers to the memory locations where the converted numbers will be stored. To obtain a pointer to an int, you simply need to prefix its name with an & sign.

    The return code from scanf() is important. It will tell you how many items the function managed to convert successfully. Many newbies miss this, and continue to ignore the return code, and then wonder why their programs give unexpected results when a user enters a non-number. So, if you learn just one thing from this page, make it this: Always check the return code from scanf().

    Here's is an example program that will read two numbers from the user and echo them back.

    
    #include <stdio.h>  
    
    int main(void)
    {
      int i,j;
      
      printf ("Input 2 numbers: ");
    
      if (scanf("%d%d", &i, &j) == 2)
        printf ("You entered %d and %d\n", i, j);
      else
        printf ("You failed to enter 2 numbers\n");
      
      return(0);
    }
    
    

    There are two problems with using scanf() to get a number:

    First, validation/error handling is poor. If the user enters 1234ZZ as their number, scanf() will read 1234 into the int variable, and leave the ZZ in the input buffer. Although the user has entered what appears to be an invalid number, the program hasn't actually noticed, and will continue processing as if all is well.

    The second problem is that of leaving characters in the buffer, in particular the newline character tends to catch people out. To show what I mean, here is some sample code that uses scanf() to get a number, followed by fgets() to get a string. As you may of heard already, fgets() is designed for reading strings, but cannot read numbers directly like scanf() can.

    
    #include <stdio.h>   
    
    int main(void)
    {
      int i;
      char name[BUFSIZ];
      
      printf ("Input a number: ");
      scanf("%d", &i);
      
      printf("Enter your name: ");
      fgets(name, BUFSIZ, stdin);
      
      printf ("Hello %s, your number was %d\n", name, i);
      
      return(0);
    }
    
    /*
     * Program output:
     Input a number: 9898
     Enter your name: 
     Hello 
     , your number was 9898
     *
     */
    

    As you can see from the output, the newline character that was left in the input buffer by scanf() was read in by fgets(), and gave the appearance that fgets() never actually run.

    You could scanf() to input your string, but I would advise against it. See the FAQ on reading strings from the user to learn more.

    To clear the input buffer, you have to read through the input until there is no more. Sometimes you'll see people recommending the use of fflush(stdin), but this is undefined behaviour, and is therefore bad practice. Instead, use something like the following, a looping input function that eats characters until the desired end of line marker is reached:

    while (getchar() != '\n');

    As a final example for this section, study the following snippet of code. It does its best to ensure that the user enters a valid number before printing its value.

    
    #include <stdio.h> 
    
    int main(void)
    {
      int temp;
      
      printf ("Input your number: ");
    
      while (scanf("%d", &temp) != 1)
      {
        while (getchar() != '\n');
        printf ("Try again: ");
      }
    
      printf ("You entered %d\n", temp);
      
      return(0);
    }
    
    


    OPTION 3 Read As A String and Convert

    This is probably the safest and most controllable way to read in a number. As usual, there's good ways and bad ways to implement it though, and I'll show you both.

    The fgets() function will be used to get the string from the user. Make sure you understand how that works before continuing here.

    The atoi() function is commonly used to convert a string to an integer, as its name suggests - ASCII to Integer. This is a simple function to use and understand, but it doesn't provide very good error handling. This will be used as an example of the bad method.

    The prototype:
    int atoi(const char *nptr);

    This function takes a char pointer as its only parameter. This pointer should point to the string you want to convert from. The return value from atoi() will be the numeric value. The following code shows how to implement this function.

    
    #include <stdio.h> 
    #include <stdlib.h> 
    
    int main(void)
    {
      char buf[BUFSIZ];
      int i;
      
      printf ("Enter your number: ");
      
      if (fgets(buf, sizeof(buf), stdin) != NULL)
      {
        i = atoi(buf);
        printf ("You entered %d\n", i);
      }  
      
      return(0);
    }
    
    

    The problem with using atoi() is that no error checking is performed. If the user enters something other than a number, atoi() simply stops parsing the array, and returns whatever value it has already accumulated. If it hasn't accumulated any numbers, it will return 0. The follwing table shows some examples of user input, and the appropriate return value from atoi():

    
    Input | atoi()
    ------+-------
    11      11
    34ABC   34
    ZXY     0
    
    

    The following functions are also available for use, and perform the same operation as atoi(), but on differing number types: atof() for floats, and atol() for long ints.

    To better validate the input, the function strtol() can be used. It is slightly more complex to understand, but it's worth it.

    The prototype:
    long int strtol(const char *nptr, char **endptr, int base);

    As before, the return value is the numeric value of the string, the first parameter is a pointer to that string. The second parameter is of type "pointer to a pointer a char", which in short means the address of a char pointer. This may sound confusing, but a little explanation should make things clearer.

    When strtol() is parsing the array looking for a numbers, it will be stopped by non-number characters, exactly like atoi(). The pointer endptr, is updated to point to the character that caused the parsing to stop. The invoking function can use this information to determine if the input is valid or not.

    The final parameter of strtol() denotes the base of the input string, which makes this function useful for inputting numbers in say base 16, hexadecimal.

    Here is an example of this function in action.

    
    #include <stdio.h> 
    #include <stdlib.h> 
    
    /*
     *  An example of how to use strtol() to read a number
     *  and validate that one was entered correctly.
     *
     */
     
    int main(void)
    {
      char buf[BUFSIZ];
      char *p;
      long int i;
      
      printf ("Enter a number: ");
    
      if (fgets(buf, sizeof(buf), stdin) != NULL)
      {
        i = strtol(buf, &p, 10);
        
        /*
         *  If the first character of the buffer is \n, the user 
         *  pressed [Enter] with entering any text at all, which
         *  is therefore invalid.
         *  
         *  The pointer p has been updated by strtol() to point to
         *  the first invalid character after the number.  
         *  If this character is \0 it means we reached the end of
         *    the array successfully, so we received a good number.
         *  If this character is \n it also means we reached the
         *    end of the input successfully.  This is a symptom of
         *    using fgets() to obtain the string, but does not 
         *    represent a problem.
         *  If this character is anything else, it means there was
         *    some additional characters entered after the number.
         *    In this sample program, I have deemed this situation
         *    to be invalid, however, in your program it may be 
         *    valid, depending on what you're expecting from the user. 
         *
         */
    
        if (buf[0] != '\n' && (*p == '\n' || *p == '\0'))
          printf ("Valid number of %ld entered\n", i); 
        else  printf ("Invalid number entered\n");
      }  
      
      return(0);
    }
    
    /*
     * Program output:
       Enter a number: 212121
       Valid number of 212121 entered
    
       Enter a number: asas
       Invalid number entered
     *
     */
    
    

    Here is another implementation, basically the same, but shows how to make use of the updated endptr.

    
    #include <stdio.h> 
    #include <stdlib.h> 
    
    /*
     *  An example of how to use strtol() to read a number
     *  and validate that one was entered correctly.
     *
     */
     
    int main(void)
    {
      char buf[BUFSIZ];
      char *p;
      long int i;
      
      printf ("Enter a number: ");
      
      if (fgets(buf, sizeof(buf), stdin) != NULL)
      {
        i = strtol(buf, &p, 10);
        
        if (buf[0] != '\n' && (*p == '\n' || *p == '\0'))
          printf ("Valid number of %ld entered\n", i); 
        else  printf ("The number was %d, followed by %s\n", i, p);
      }  
      
      return(0);
    }
    
    /*
     * Program output:
       Enter a number: 123abc
       The number was 123, followed by abc
     *
     */
    
    

    There are similar functions that work with other number types, they are strtod() for string to double and strtoul() for string to unsigned long.

    OPTION 4 Custom Validation

    Another method of getting a number, similar to the previous section, is to read the input as a string, then parse and validate it with custom built functions. The advantages here is that the program can be extended to handle more than just a simple number. For now though, I'll stick to validating a single number, as per the previous sections.

    The function I'll use is isdigit().

    The prototype:
    int isdigit (int c);

    This function checks a single char to see if it is a valid number (0 - 9). If the input is in a char array, simply looping through that array checking each character with isdigit() will determine if the array is a valid number or not.

    Here is some sample code, courtesy of Prelude:

    
    #include <stdio.h> 
    #include <ctype.h> 
    #include <stdlib.h> 
    #include <string.h> 
    
    int validate ( char *a )
    {
      unsigned x;
      for ( x = 0; x < strlen ( a ); x++ )
        if ( !isdigit ( a[x] ) ) return 1;
      return 0;
    }
    
    int main ( void )
    {
      int i;
      char buffer[BUFSIZ];
      printf ( "Enter a number: " );
      if ( fgets ( buffer, sizeof buffer, stdin ) != NULL ) {
        buffer[strlen ( buffer ) - 1] = '\0';
        if ( validate ( buffer ) == 0 ) {
          i = atoi ( buffer );
          printf ( "%d\n", i );
        }
        else
          printf ( "Error: Input validation\n" );
      }
      else
        printf ( "Error reading input\n" );
      return 0;
    }
    
    

    This concludes the number reading FAQ.

    I hope this FAQ has been of use to you. You can find out more about some of the functions used in the examples by viewing their man pages, which I strongly suggest you do.


    Written by: Hammer

  • Script provided by SmartCGIs