Very early on when attempting to learn programming in C, you often do exercises that read in and print out strings and numbers. Now you might assume that C has a simple function that will get user input, and indeed it does, but there are a couple of gotchas that can easily trip up those new to programming in C.
There are two things to vigorously avoid, and unfortunately they are all too commonly seen:
char text[20];
gets(text); /* NEVER USE gets() */
scanf("%s", text); /* MANY HIDDEN GOTCHAS */
So how do you do it right? The short answer is to use fgets
to read a line of text. However, please note that when you enter text, you press the [Enter] or [Return] key and this character does not just vanish, it remains in the input buffer.
For example:
#include <stdio.h>
int main()
{
char text[20];
fputs("enter some text: ", stdout);
fflush(stdout); /* http://c-faq.com/stdio/fflush.html */
fgets(text, sizeof text, stdin);
printf("text = \"%s\"\n", text);
return 0;
}
enter some text: hello world
text = "hello world
"
So when reading strings, you often want to remove this newline. Be careful though, because it is not always there! When the incoming text is one less than the size of the buffer, the newline is retained. You can search for the newline and overwrite it with a null terminator.
--- skimmers start here ---
#include <stdio.h>
#include <string.h>
int main()
{
char text[20];
fputs("enter some text: ", stdout);
fflush(stdout);
if ( fgets(text, sizeof text, stdin) != NULL )
{
char *newline = strchr(text, '\n'); /* search for newline character */
if ( newline != NULL )
{
*newline = '\0'; /* overwrite trailing newline */
}
printf("text = \"%s\"\n", text);
}
return 0;
}
enter some text: hello world
text = "hello world"
There are many other ways to do this, but this is very frequently recommended and always correct.
--- skimmers stop here ---
If you are reading a number, then first read in the input string and consequently follow it with a call to sscanf
or strtol
to convert it to a number.
#include <stdio.h>
int main()
{
char text[20];
fputs("enter some number: ", stdout);
fflush(stdout);
if ( fgets(text, sizeof text, stdin) )
{
int number;
if ( sscanf(text, "%d", &number) == 1 )
{
printf("number = %d\n", number);
}
}
return 0;
}
enter some number: 42
number = 42
The newline does not need to be removed, unless you choose to, because it is whitespace and is not part of a valid number. You may find the following related code snippets regarding the input of numbers and text useful:
- Read a Line of Text from the User
- Safe Version of gets()
- Read a Line of Text from the User, Discard Excess Characters
- Read an Integer from the User, Part 1
- Read an Integer from the User, Part 2
- Read an Integer from the User, Part 3
- Read a Floating-Point Value from the User, Part 1
- Read a Floating-Point Value from the User, Part 2
To recap, here is why not to do what I said not to do! First, never use gets
. It is included as a standard library function only as a holdover from pre-C89 code (the standards committee opted in the interest of not breaking existing code). But it is inherently and notoriously unsafe because there is no way to tell it the size of the buffer into which data will be read. So it is always a risk for buffer overflow , which may be used as an exploit . If you ever want to be employed as a programmer, it's best not to ever let a potential employer see you use this function!
As for scanf
it is a truly complicated beast, and should certainly not be the first choice for someone just starting out programming in C. One of the first issues with scanf("%s", str);
is it suffers from a very similar problem as gets
, which is the incoming buffer size is not specified. If you didn't look up the description of %s
you may not have known that this directive is whitespace-delimited. Which means that it if the user enters hello world
, then str will be hello
. There are more gotchas, such as if you are also using scanf
to read integers, perhaps using scanf("%d", &i);
, then the same whitespace issue may bite you. Remember that when you enter text you press the [Enter] or [Return] key and it remains in the input buffer. This can cause issues when mixed with other input functions such as getchar
. There is also another insidious issue that comes up when using scanf
: the "need" to flush the input buffer.
And don't think I presented a comprehensive list of gotchas associated with scanf
, these were merely some common ones!