So far the programs you wrote didn't interact very much with the user. In the last chapter you learnt how to read characters from the keyboard. But that isn't very good for, say, getting data values like numbers. Then, so far you didn't really have any way of presenting the output particularly well formatted. We'll deal with these issues, to some extent.
So far we've only seen basic uses of printf. Actually printf is capable of pretty handy formatted output (that's what the 'f' stands for!). First lets write a program that converts temperatures from fahrenheit to celsius. Presenting the first program of this chapter:
#include <stdio.h>
#define LOWER 0
#define UPPER 100
#define STEP 10
main()
{
float Cels, Fahr;
for (Fahr = LOWER; Fahr <= UPPER; Fahr += STEP)
{
Cels = (Fahr - 32) / 5.0 * 9.0;
printf ("%f %f\n", Fahr, Cels);
}
}
Before we discuss the output of the program, let me explain the #define statements.
#define <identifier> <value>
The first word (UPPER, LOWER & STEP) is an identifier. It follows the same naming rules as variables. Everything on that line after the identifier is the value. Now, before compiling the program, the compiler directly inserts the value of the #defined identifier. The value is constant i.e. can't be changed by the program. You'll learn more about these things in a later chapter. It is only a convention to name constants in all capitals, not a requirement.
Notice that we haven't terminated the #define statements with a semicolon. That is because everything on the line after the identifier is taken as the value, so the semicolon would be to. If we had used semicolons, what the compiler would see (after making the insertions) is:
for (Fahr = 0;; Fahr <= 100;; Fahr += 10;)
If you tried to compile the program, you would get a syntax error on the for line, not the #define line. You'll learn more about #define and other pre-processor directives (#define is called a pre-processor directive, because it modifies the code before the actual compiler sees it) in a later chapter.
We've used the constants to avoid embeding meaningless numbers in the program. That way one knows what the number means. Consider a long program, where these numbers are used in several places. If you ever need to change the limits, you only need to modify it at one place, i.e. the #define statement.
When you run the program, you'll see something badly like:
0.000000 -17.777779
10.000000 -12.222222
and so on. The numbers are not right justified, there are six digits after the decimal point and so on. Even if those digits are only zero. Anyway, do you really want the celsius temperatures to six decimal place accuracy? Surely one or two is enough. And you don't need any decimal placed for the fahrenheit temperatures, because they're all integers. Its surprisingly easy to put all of this right. You only need to change the printf statement to:
printf ("%3.0f %7.2f\n", Fahr, Cels);
The only change made is to insert 3.0 and 7.2 between the % and f. What does it mean? The 3 (or 7) is the field width. The 0 (or 2) is the precision. A period (.) is used to separate the two.
The precision specifies the number of digits after the decimal point. The output is right justified in a field of width specified by the 'field width'. This includes the sign, if any, digits before and after the decimal point, and the decimal point itself. If the output produced is larger than the field width, the field width is ignored. Note that, is won't put zeroes to the left of the decimal point to justify the number... instead, spaces are inserted. (You can change that, as you'll see later in this chapter)
You can also specify only one of the two. For example, %10f will justify it in a field of ten, but won't change the number of digits after the decimal point. On the other hand, %.3f will only round the number to three decimal places.
The field width specifier can be used with any kind of data and has the same effect. The precision specifier can be used with most types of data. Its effect depends on the kind of data you're dealing with.
Enough bits and pieces of the printf format string. Now, lets get to the general form of a format specifier:
%{flags][field_width][.precision][input_size]type
A format specifier begins with a % and ends with a type character. All other fields are optional. Lets first look at available type characters for all data types we've encountered so far. This list is not complete. I will provide a more comprehensive list later on, when the time comes.
Character | Use |
---|---|
d, i | Decimal integer (int) |
x | Hexadecimal number, using small letters (abcdef). Does NOT prefix 0x. |
X | Same as above but uses capital letters (ABCDEF). |
u | Unsigned integer (unsigned) |
c | Character (Char) |
f | Floating Point number (float). Printed in normal decimal form [-]mmm.ddd |
e, E | Same as above, but printed in the form [-]m.dddddde±xx or [-]m.ddddddE±xx |
g, G | Uses %e or %E if exponent if less than -4 or greater than the value specified by precision. Uses %f otherwise |
% | Prints a % sign. Does not convert any arguments. |
You must specify exactly one type character in each format specifier.
The precision field has different effect depending on the type character.
Type | Effect of Precision |
---|---|
e, E, f | Number of digits after the decimal point. Trailing zeros are added or the number is rounded if required. If no precision is specified 6 is used. |
g, G | Number of significant digits printed. If no precision is specified 6 is used |
d, i, x, X, u, o | Minimum number of digits printed. Leading zeros are added if required. |
The input size modifier can be either l, L or hA period (.) is used to separate field width from precision. If you want you can use only one of them in a format specifer. If you specify just a number, it will be interpreted as the field width. If you specify a number preceded by a period, it will be interpreted as precision.
Type | Modifier | Effect |
---|---|---|
d, i,x, X, o, u | h | Argument is interpreted as short, rather than int or unsigned |
l | Argument is interpreted as long int (i.e.)long | |
f, e, E, g, G | l | Argument is interpreted as double, rather than float |
L | Argument is interpreted as long double |
Finally, we come to the flag character. The order in which flags are gives is of no significance.
Flag | Effect |
---|---|
- | The output is left justified in the field, rather than right justified i.e. trailing, not leading spaces are added |
+ | Specified that a sign is always printed. Normally a sign is not printed for positive numbers |
space | A space is printed before positive numbers, instead of a + sign |
0 (zero) | For numeric arguments, leading zeros are added instead of leading spaces |
# | Uses an alternate form or printing. For o a zero is prefixed. For x or X, 0x or 0X is prefixed. For floating point numbers, a decimal point is printed even if there are no digits after the decimal point. For g and G trailing zeros are not removed. |
Only one small point remains to be explained. The two numerical specifiers, i.e. field width and precision can also be specified using variables. Put a * in the format string in place of either or both. The value is then taken from the next argument on the argument list. This argument must be of type int or float.
/* printflt.c
Prints the floating point number 123.456 in a number of ways to show how various format specifiers work.
*/
#include <stdio.h>
main()
{
double f=123.456;
printf ("\nf = 123.456\n\n");
printf ("%%f: %f\n", f);
printf ("%%e: %e\n", f);
printf ("%%E: %E\n", f);
printf ("%%g: %g\n", f);
printf ("%%G: %G\n", f);
printf ("%%20f: %20f\n", f);
printf ("%%.2f: %.2f\n", f);
printf ("%%10.2e: %10.2e\n", f);
printf ("%%.2g: %.2g\n", f);
printf ("%%.4g: %.4g\n", f);
printf ("%%+f: %+f\n", f);
printf ("%%-20f: %-20f", f);
printf ("%%020f: %020f", f);
printf ("%%*.*f: %*.*e\n", 20, 2, f);
}
Note the last line of the example program, where the field width and precision are specified in the argument list, using *. Although we have used integer constants here, we might as well have used variables, in place of 20 and 2.
The example program only demonstrates formatting a floating point number. You should write similar programs to see how other types are formatted. The printf statement is fairly complex and is used very frequently in C programming. Spend as much time as you need, here, and experiment with all kinds of format specifiers and data types.
It is important that there are enough arguments for the specifiers in the format string, and that they are in the correct order, otherwise the results are undefined. You will usually end up with garbage. If there are too many arguments, they are simply ignored.
When you call scanf it actually reads in a complete line of text from the keyboard. Then it gets values from that line of text until the line is exhausted. Then it reads the next line, and so on. However, one call to scanf will read in only one line. If it doesn't find all the values required, the corresponding variables won't be assigned anything.& specifies a pointer to a variable. Pointers are a topic central to C and will be dealt with in a separate chapter. For now, just remember to prefix scanf arguments with an ampersand.
The following examples show the usage of scanf and the expected input:
scanf ("%d", &x);
/* Expected input: integer */
scanf ("%d %d", &x, &y);
/* Two integers on the same line, separated by spaces or any other non-numeric characters */
scanf ("%d%d", &x, &y);
/* Same as above */
scanf ("%d %x %f", &x, &y, &f);
/* An integer, a hexadecimal integer and a floating point number */
scanf ("Serial:%d", &x);
/* The word 'Serial' and a colon (:) followed by an integer. */
Note that in the last example, a number entered is not assigned to x unless it is preceded be the word Serial and a colon. Even the case must be correct. Such things make it all to easy for the user to enter thigs wrong, so your better of not including any such text in the format string for scanf.
It is important to note that you can't specify a prompt in the scanf statement. You should precede a scanf with a printf. For example,
printf ("Enter Serial No.:");
scanf ("%d", &x);
We've already dealt with Character Input in the chapter on Control Flow. The getchar() function can be used to read from the keyboard once character at a time. There is an analogous putchar() function. It accepts a character argument, and writes it to the screen. Note that, while getchar() is buffered, i.e. it reads a whole line at a time, putchar() will immediately put the character on the screen. You'll get a better understanding of this if you run the following program-
#include <stdio.h>
main()
{
char c;
do
{
c = getchar();
putchar (' ');
if (c != '\n')
putchar (c);
} while (c != '\n');
}
This shows that it reads in the whole line, but prints it one character at a time. We never print the newline character, so if putchar() buffered a whole line before printing it, we wouldn't see any output!
Another point to note is that the first line of characters, (that you see while typing) is the keyboard input being echoed. It is produced by getchar() while buffering the line, NOT by putchar(). The second line is produced by putchar(). You can verify this by commenting out the putchar() statements.
As always, before moving on the the next chapter, its time to make sure you've understood everything covered so far. Experiment with printf and scanf and find out what you can do. Make sure you understand the effect of all the different parts of the format specifier on different data types. What will happen if you try printing a character as an integer?
If you've tried out all the experiments mentioned above, and are sure you've understood everything, its time for the next step.
C Tutorial Quicklist | Start Page
| Contents |
| Ch 1:Introduction
| Ch 2:Variables
| Ch 3:Loops
| Ch 4:Expresisons
| Ch 5:Control Flow |
| Ch 6:Basic I/O
| Ch 7:Arrays |
| Home Page
| Beginners
| Advanced
| Links
| Downloads
| Books |
| C Tutorial
| HYPERSearch
| Guestbook
| Feedback |