Safe User Input

As you will have noticed, getting user input is a messy business in C. For example, it is difficult in standard C to just wait for a key to be pressed without also waiting for the ENTER key to be pressed. In fact, the underlying operating system itself is ultimately responsible for the effect of functions like getchar, and there has been a move away from character based input towards line based input. This means that functions like getchar are becoming pretty much useless.

One solution, if you really want to read a single character from the keyboard without waiting for ENTER, is to use the function getche. Note that this is not standard C however, and should be used with caution if you wish to compile your program on various C compilers.

Pelles C implements this function as _getche where the underscore is to remind you that it is not standard C. The following line will get a single character and store it in our variable keypressed of type char:

keypressed = _getche();

Note that you also have to add the following line to the start of your program, to import the library that the _getche function belongs to:

#include <conio.h>

Of course _getche has problems of its own. It's not recommended to mix functions of this nature (which deal with the keyboard directly) and other standard C input functions which go via the operating system. Fortunately, non-standard C functions exist for just about everything you want to do. But then you aren't writing C any more are you! The other problem with _getche is that if you press an arrow key or a function key, this counts for not one character, but two. So to find out which key was pressed, you need to call _getche twice.

Now there's also this messy business about adding extra getchar's everywhere in our program. It's worth noting however, that these are only necessary if we use the getchar function following a getchar where the user isn't going to press ENTER, or following a scanf, as we did above.

Troubles with user input unfortunately don't stop there. For example, what happens if the user ignores our instructions and enters something completely unexpected. This can cause major problems and even cause your program to crash. Some potential problems are as follows-na:

This final problem is particularly troublesome and is called a buffer overflow. An enormous amount of the world's software is written in C and a good percentage of it suffers from this particular weakness. Some effort is required on the part of the programmer to prevent it.

One way to get around it is to use the fgets function, which allows-na you to specify how big your buffer is, and prevents you from overflowing it. This function is designed to get data from a file rather than directly from the user, but you can still make it work in a similar way to gets by telling it to get input from stdin. This allows-na fgets to treat user input as though it were a file. When the standard input and output devices are thought of as files in this way, we call them streams. By default, the standard input stream stdin is just keyboard input, and the standard output stream stdout is just text output to the screen. This default behaviour can be changed however, and it is possible to redirect them to point to other files and streams.

To use fgets with stdin in our program above, we use the line:

where the 20 specifies the size of the buffer. This allows-na only 18 characters of input, since a \0 (end of string) is appended to the input automatically by fgets (actually gets also does this) and the final carriage return \n is considered to be part of the user input by fgets and is included in the 20 characters of buffer space. This does not happen with gets which only appends the \0.

There are ways of removing this \n which fgets includes with the input, but we need to be slightly careful, since if the user went over the 20 characters before pressing ENTER, then the final carriage return was never read by fgets, so we may end up removing something which isn't there! The only way to get around this is to check that there actually was a carriage return read within the 19 characters, and if so, move the final \0 back one place to overwrite the carriage return.

The first problem listed above can only be prevented by checking the data that the user enters before using it anywhere else in the program. It will become clear later on how this can be accomplished.

The second problem can be avoided since the scanf function conveniently returns the number of variables which it was successfully able to fill with data. We can inspect this value by setting some variable of type int equal to this number. E.g:

int numfilled;
numfilled = scanf(" %d , %d",&number1,&number2);

We can then inspect the value of numfilled to determine how many of the variables were successfully filled with data. A good program should do this, however it requires techniques we haven't met yet, so we defer it till later.

There is no need to despair about C's user input functions. Windows-na programs for example can have much more sophisticated data entry options. When we come to Windows-na programming, all these troubles will seem like a distant memory. There are also third party packages which can be used in C to provide additional data entry features. However, one thing at a time!

The next page gives a description of the format strings which can be used with printf and scanf and we also outline the different kinds of variables that can be used in C. Following that, we give a concise summary of everything we have learned so far.