C Input & Output
Using printf and scanf

Note: You need to know all but the last example (self-replicating programs) for exams.


Introduction: C uses a special "print formatted" function printf which is powerful and handy, but not type-safe (explained below). This function is now in the latest version of Java (1.5.0) as System.out.printf. C has a similar useful but extremely error-prone function for reading formatted data: scanf.
Primitive Data Types: C supports the following primitive data types. First the ones we will mostly use in this course:

Then the ones we will seldom if ever use. (The unsigned types are too specialized.  long is usually the same as int, so it is not needed.  short is too short to be of much use.  float has far too little precision to be recommended for use.)


Formatted output using printf: Consider the following interactive session in Linux or Unix:

Use of printf to print Pi
% cat pi.c
#include <stdio.h> /* for printf */
#include <math.h>  /* required for atan */

int main( ) {
   double pi = 4.0*atan(1.0);
   printf("Pi = %20.16f\n", pi);
   return 0;
}
% cc -o pi pi.c -lm # -lm for math library
% pi                # now execute pi
Pi =   3.1415926535897931

The function printf must always have an initial argument that is a string (in double quotes, the control string). This is followed by zero or more variables or expressions, whose values are printed according to the control string: Ordinary characters in the string are printed as they are, but a conversion specifier triggers the output of the next value in the list following the initial control string. Each conversion specifier begins with a percent character (%), which is not printed itself. The conversion specifier above is %20.16f, which controls the output of the value of the variable pi. The % starts the conversion specifier. Then the 20 says to use 20 or more columns for output. The .16 specifies 16 digits to the right of a decimal point, and finally the f says to print a float or a double in a fixed decimal format. The last character in the string above is a newline (\n) which must be explicitly present to skip to a new line at the end.

Here are two tables: conversion specifiers and special excape sequences:

printf Conversions
Conversion
specifier
Variable
type
Output
type
%i, %dint int
%li, %ldlong int long int
%f, %e, %E,
%g, %G
float,
double
double
%ccharchar
%schar *
(string)
string
    
Excape Sequences
SequenceNameAction
\nnewlineMove cursor to next line
\ttabMove cursor to next tab
\"double quotePrint double quote
\'single quotePrint single quote
\\backslashPrint a backslash
\?questionPrint a question mark
%%percentPrint a percent

A minimum field width and decimal precision can used to create formatted output.  Left or right justification can be managed as well. Here are a few examples, where an underscore stands for a blank:

int x = -145;
SpecifierOutput
%i-145
%4d-145
%3i-145
%6i_ _-145
%-6i-145_ _
%06i-00145
    
double x = 157.8926
SpecifierOutput
%f157.892600
%6.2f157.89
%+8.2f_+157.89
%7.5f157.89260
%e1.578926e+02
%.3E1.579E+02
%6.0f_ _ _ 158

Don't worry about g and G in this course (they combine features of both f and e).


printf is not type-safe: In case the conversion specifier does not correctly match the type of the variable, printf makes mistakes (gives incorrect answers) that the C compiler does not detect. For example, in the previous example of pi.c suppose the %20.16f specifier is written as %20.16i by mistake.

Mismatch of specifier and type in printf
% cat pi2.c
#include <stdio.h> /* for printf */
#include <math.h>  /* required for atan */

int main( ) {
   int a = 314159, b = 271828;
   double pi = 4.0*atan(1.0);
   printf("%20.16i, %i, %i\n", pi, a, b);
   return 0;
}
% cc -o pi2 pi2.c -lm # compile on linux
% pi2                 # run on linux
    0000001413754136, 1074340347, 314159
% cc -o pi2 pi2.c -lm # compile on a Sun
% pi2                 # run on a Sun
    0000001074340347, 1413754136, 314159
% lint -err=warn -Ncheck=%all -Nlevel=4 -Xarch=v9 pi2.c

name used but not defined
    atan                pi2.c(6)

function returns value which is always ignored
    printf

function argument ( number ) type inconsistent with format
    printf (arg 2)      double  :: (format) int         pi2.c(7)

.c file has no corresponding .h file
    /home/wagner/public_html/CS2213/lectures/pi2.c

Notice what has happened here. We expected the variable pi to be printed incorrectly, since it had the wrong specifier, but we expected the next two variables a and b, to be printed as 314159 and 271828, repectively. We see that pi is indeed printed as garbage, but a is also printed as garbage, and b is printed as if it had a's value. Also, running on a Sun gives different garbage than running under Linux. (In this case, just the first half of the storage is used up for the first print, and the printing is out-of-synch after that.) Notice that lint running on a Sun did correctly diagnose the error.


Formatted intput using scanf: The function scanf is used in C to read formatted data. It is much more error-prone than printf and must be used with care. It's first argument requires a control string in quotes with the proper conversion specifier(s). The next argument(s) give addresses of where the data will be stored. In C such an address is specified by the variable where the value is to be stored, preceeded by the address-of operator printf. Here is an example of the use of this function.

Use of scanf
% cat age.c
#include <stdio.h>

int main( ) {
   int age;
   double height;
   printf("Please enter age in years, and height in meters ---> ");
   scanf("%i  %lf", &age, &height);
   printf("Age: %i years, height: %5.2f meters\n", age, height);
   return 0;
}
% cc -o age age.c
% age
Please enter age in years, and height in meters ---> 23  1.68
Age: 23 years, height:  1.68 meters
% age
Please enter age in years, and height in meters ---> 23

1.697
Age: 23 years, height:  1.70 meters
% age
Please enter age in years, and height in meters ---> 23  2
Age: 23 years, height:  2.00 meters
% age
Please enter age in years, and height in meters ---> 23.5  2
Age: 23 years, height:  0.50 meters
% age
Please enter age in years, and height in meters ---> xyz  1.68
Age: -1073744696 years, height:  5.12 meters

Notice about the above program and its runs:

Here are several conversion specifiers. Note that long int and double require an extra l (letter ell) in the conversion specifier.

scanf Conversions
Variable typeConversion specifier
int%i, %d
long int%li, %ld
float%f, %e, %E, %g, %G
double%lf, %le, %lE, %lg, %lG
char%c

A common mistake arises because printf uses %f for a double, but scanf uses %lf (percent-ell-eff) for a double Another common mistake is to leave the ampersand off the variable being stored into, usually producing a segmentation error when you execute. scanf ignores whitespace characters such as a newline in its format string.

Later the section on strings will present the functions sprintf and sscanf, which print to a string and scan from a string, respectively -- very useful when needed.

Open, study and execute fahrcels.c


The "magic" & on the actual parameters in scanf: As mentioned above, scanf uses the "address of" operator & on each of its actual parameters. This allows scanf to provide a value back to the calling function. Thus after a call such as: scanf("%i %lf", &m, &x);, values will "pop into" the variables m and x. When we pass the address of the variable m, using &m, C allows the called function (scanf in this case) to place a value into the variable at that address, that is, directly into m. C does this by declaring the corresponding formal parameters to be addresses rather than ordinary types.

A formal parameter that is meant to be the address of an int is declared with an extra *, that is, int *. To access the actual value at the address of this formal parameter, you use the dereference operator: another *. So if the formal parameter is declared int * mp (pointer to m), then *mp would refer to the actual value. Instead of scanf (which is a little complicated to show here), if we were trying to get values back for the variables above, say from a function myfetch, the program would look as follows:

Addresses as parameters: fetch.c
% cat fetch.c
#include <stdio.h>

void myfetch(int *mp, double *xp);

int main( ) {
   int m; /* unititialized! */
   double x; /* unititialized! */
   myfetch(&m, &x);
   printf("m = %i, x = %10.6f\n", m, x);
}

void myfetch(int *mp, double *xp) {
   *mp = 666;
   *xp = 2.718281828;
   return;
}
% cc -o fetch fetch.c
% fetch
m = 666, x =   2.718282


Just for fun ... Here is a program (from C: A Reference Manual, 4th ed., by Harrision and Steele, page 378) that will reproduce itself: if compiled and executed, the output will be a copy of the source program itself!


Copyright © 2011, Neal R. Wagner. Permission is granted to access, download, share, and distribute, as long as this notice remains.