C Input & Output
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:
char: a single byte, signed.
It is treated exactly like an 8-bit signed integer.
int: a signed integer, system dependent, often 4 bytes,
that is, 32 bits.
double: a floating point value, system dependent,
often 8 bytes, that is, 64 bits.
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.)
short: a short signed integer, system dependent, often 2
bytes.
long: a long signed integer, system dependent, often also
4 bytes.
unsigned char: an unsigned version of char.
unsigned short: an unsigned version of short.
unsigned int: an unsigned version of integer.
unsigned long: an unsigned version of long.
float: a floating point value, system dependent, often 4
bytes.
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
", 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
(
) 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, %d |
int |
int |
| %li, %ld |
long int |
long int |
%f, %e, %E,
%g, %G |
float,
double |
double |
| %c |
char |
char |
| %s |
char *
(string) |
string |
|
|
| Excape Sequences |
| Sequence |
Name |
Action |
|
|
newline |
Move cursor to next line |
| |
tab |
Move cursor to next tab |
| " |
double quote |
Print double quote |
| ' |
single quote |
Print single quote |
| \ |
backslash |
Print a backslash |
| ? |
question |
Print a question mark |
| %% |
percent |
Print 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; |
| Specifier |
Output |
| %i |
-145 |
| %4d |
-145 |
| %3i |
-145 |
| %6i |
_ _-145 |
| %-6i |
-145_ _ |
| %06i |
-00145 |
|
|
| double x = 157.8926 |
| Specifier |
Output |
| %f |
157.892600 |
| %6.2f |
157.89 |
| %+8.2f |
_+157.89 |
| %7.5f |
157.89260 |
| %e |
1.578926e+02 |
| %.3E |
1.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
", 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
", 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:
- A
double requires a
%lf as conversion specifier,
rather than just %f, which is used
on input.
- The function scanf
keeps processing input characters until it finds the items it is supposed
to look for.
- Even with erroneous input (last example), it doesn't give a segmentation
violation, or other error, but just puts wrong values into the variables.
- In processing input, scanf
will skip over whitespace, such as newlines and tabs.
- In the fourth input above, scanf
was looking for an int followed
by a double, and
23.5 2 appeared on input.
The 23 worked for the
int, and it was terminated
by a dot. Then the .5 ,
terminated by a blank was taken as the double,
not what was intended.
- On the previous input, a 2
terminated by a blank was converted to double.
Here are several conversion specifiers.
Note that long int and
double require an extra
l (letter ell) in the conversion specifier.
| scanf Conversions |
| Variable type |
Conversion 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
", 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!
Created By Dr. Neal Wager and Mike Maltrud: The University of Texas at San Antonio