Reading Data with scanf
Textbook Background
The C standard library allows us to read formatted data in much the same way we display it. The textbook provides background:
- King: Section 3.2, pages 42-46, OR
- Kernighan & Ritchie: 7.4, B.1.2, B.1.3
Introduction to scanf
The scanf function (in the stdio.h library)
directs the machine to read values for one or more variables from the keyboard.
For each desired value, scanf requires
- information in a format string indicates how the characters typed by the user will be interpreted (e.g., as an integer, as a double, etc.), and
- the address of a variable where the value read is to be stored.
Notes:
-
As with
printf, format information is collected together as an initial string parameter. -
Using our experience from previous work with functions and parameters,
parameters to
scanfinvolve reference parameters (e.g., with an ampersand &), so the functionscanfcan place data at the location of anintordouble.
When a user enters information into a program, the user types a sequence of characters. Sometimes this information is intended to be a string of characters, such as a name or an address. In other applications, a sequence of characters, such as 123.45, should be interpreted as a number.
When characters are to be considered as numbers, input can follow either of two basic forms:
-
The program can proceed in two steps:
- read the information as a sequence of characters
- convert the character sequence to a number
- the program can rely upon a library function to perform both steps as one logical operation.
The library function scanf is commonly used for the latter
approach. Using scanf involves several elements. The basics of
this work are illustrated in the following code segment:
double a, b;
scanf ("%lf", &a);
scanf ("%lf", &b);
As illustrated in this segment,
-
The first parameter for
scanfindicates the format of data to be read:-
%lffor double precision real numbers (lfstands for "long float") -
%ffor single precision real numbers (floating point numbers) -
%dfor decimal integers -
%cfor reading a character -
%sfor reading a string and%Ns, where N is a positive integer, for reading up to N characters
-
-
Use an ampersand
&before the variable to represent the "address" of the variable (the location where the value should be stored). -
When reading a number,
scanfskips initial whitespace (spaces, tabs, newline characters). -
After skipping whitespace,
scanfreads as long a string of digits as it can to obtain a number. Thus, if one enters123Walker
thescanfstatement will read the number as123;"Walker"is not part of a number, so reading of the number cannot proceed. -
If
scanftries to read a number but encounters non-numeric data after whitespace, then reading stops and the number is assigned the value 0. -
The above code segment skips whitespace (if any), reads a first
number (up to whitespace or non-numeric data), and assigns the number read to
variable
a. This process then is repeated in reading variableb.
scanf allows the two reading operations above to be combined
within a single statement as follows:
double a, b;
scanf("%lf%lf", &a, &b);
Beyond the identification of variables and formats for reading, the
scanf can specify other characters that must be part of the
input. For example, suppose a program is supposed to read hours and
minutes in the format hour:minutes:seconds, such as
12:34:56 or 5:8:27. In this setting, the user is
supposed to enter the colon character between integer numbers. The
following code segment would perform such a read operation:
int hr, min, sec;
scanf ("%d:%d:%d", &hr, &min, &sec);
Two Safety-Critical Notes
On Reading Strings
As both textbook readings point out, the %s conversion
specification requires a variable of type char* to
accompany it. As we should now know well, a pointer to a character may
refer to a sequence of available memory addresses, but it does not
convey any information about how much memory is available. Thus, only
using %s puts your program at risk of causing serious
damage through a buffer overflow, because it does not
tell scanf how much data it may safely read.
Thus, you should never use the %s
conversion specification without an accompanying space limit. While we
strongly prefer to use the #define preprocessor
directive to establish buffer sizes in a single, clear manner, this
dogma does not work well with a conversion string, which must be hard
coded.
One way around this is to use a closely coupled pair
of #define directives, as in the following example.
#include <stdio.h>
/* Length and format string for character buffers (plus a null terminator) */
#define MAXSTR 127
#define SCNSTR "%127s"
int main()
{
int foo, bar;
char buf[MAXSTR+1]; /* Allocate an extra space for the null terminator */
scanf("%d " SCNSTR " %d",&foo,buf,&bar); /* C concatenates string literals */
printf("%d %s %d\n",foo,buf,bar);
return 0;
}
This approach also is potentially poor practice, because a subsequent programmer may change only one of the two, but it's less likely to be missed than if a hard-coded value is strewn throughout the program source code.
On Return Values
Note that scanf returns the number of input items
assigned, which could be fewer than expected (even zero). Thus, it is
generally useful (and highly recommended) to check the
return value to make sure that your program received the amount of
input it expected and take appropriate action if it did not.
Examples
We may discuss some of the following examples in class
