Jump to: navigation, search

Chapter 9 - The printf function

It is a policy of this tutorial that, as you take your first steps in learning the C language, each lesson builds logically on what has gone before. That is, there are (ideally) no forward references in the tutorial. Thus, we couldn't introduce puts until we'd seen how to call a function. Nor could we introduce it until we'd seen how pointers work, and we also had to know about the char type.

Just this one time, though, we're going to break this rule, and introduce some magic. This is because we are getting to the stage where we really, really need to learn about the printf function, and unfortunately this is a function that does rely on magic (i.e. something we're not ready to learn about yet). To minimise the harmful effect of this magic, I'm going to give you a brief peek behind the magician's curtain, and tell you just enough, but as little as possible, about variadic functions.

Variadic functions

So far, every function we have shown you shares an important characteristic: they all take an exact number of parameters. It might be one, it might be two, it might be more... but for any given function, it's always the same number. Because of this, we know how many arguments to provide to the function when we call it. (Arguments are what we pass to a function, and parameters are what the function receives. The clc wiki has a more detailed explanation of the difference between arguments and parameters in case you want to read more about this.)

C does, however, have a mechanism for enabling us to write functions that expect a variable number of parameters. These functions are known as variadic functions. In due course, we will see how to write our own variadic functions. For now, you need only know that they do exist, they're not actually magical, and they have to follow certain rules. Particularly, the writer of a variadic function has to design a scheme whereby he or she can tell how many arguments have been sent to the function, and what types those arguments have.

For now, that's all you need to know about variadic functions: that they exist, and that they aren't psychic -- they have to follow some kind of scheme for interpreting the information they are sent.

First steps with printf

The printf function, then, is a variadic function. It takes at least one parameter, and that parameter must be a string. (Actually, we know that a string is an array of char terminated by a null character, and we know that when we try to pass an array to a function, what actually gets sent is the address of the first element in that array. As long as we remember that this is what's happening, we can afford to be a little relaxed, and talk about "passing a string to a function".

The printf function is prototyped in stdio.h, and on this occasion I'm not going to show you the prototype. We'll cover it when we get onto variadic functions. Instead, let's start to use the function, to get ourselves comfortable with it.

#include <stdio.h> /* pull in the prototype for printf */

int main(void)
{
  printf("Hello, world!\n");
  return 0;
}

So far, so good. In the above program, printf is behaving rather like puts. We pass it a string argument, and it writes that argument onto the standard output device for us. Unlike puts, however, it will not automatically append a newline character. So if we want one, we have to add one to the string. This is a small price to pay in exchange for the fact that, if we don't want a newline, we don't have to have one.

Printing an int

We have been held back somewhat by the fact that writing an int to the standard output device is rather complicated. We had to write a special function, print_integer, to do it, and that was quite a complicated function (albeit a useful one). But the printf function can easily print an int for us, provided we ask it in the right way.

#include <stdio.h>

int main(void)
{
  int i;
  for(i = 0; i < 10; i++)
  {
    printf("%d\n", i);
  }
  return 0;
}

This program prints the numbers 1 to 10, one number per line. Why?

The first thing to notice is that we are sending not one argument, but two, to printf. The first is, has to be, must be, always will be, a string. The printf function uses this string to learn more about what you want. It reads the string, looking for % signs. When it finds any other character in the string, it just copies that character to the standard output device. But when it finds a % sign, its anthropomorphic ears prick up, and it sets to work. The % sign itself is discarded, and printf looks at what comes next. If it sees a 'd' (known as a conversion specifier), it expects another argument, an int, which it will then print on the standard output device in base ten (decimal, hence the 'd'). Once it's finished doing that, it goes back to reading the string, character by character, as before. In our example, that means it will find the '\n' character next, so it will print a newline character. Then it comes to the null terminator, so it stops and returns.

It is our job to ensure that we pass it the int that it is expecting. If we forget, the compiler doesn't even have to tell us! (It might tell us anyway, but we shouldn't rely on that.) And if we forget, printf won't know that we didn't pass it an int. It will just take some random blob of memory and print that instead, which isn't what we want to happen.

We can, if we like, print more than one int, but we need a separate "%d" for each int, as in the following example:

#include <stdio.h>

int main(void)
{
  int miles;
  int metres;
  int km;

  puts("Miles   Metres    Kilometres");

  for(miles = 0; miles < 100; miles += 10)
  {
    metres = miles * 1609;
    km = metres / 1000;
    printf("%d     %d     %d\n",
           miles, metres, km);
  }
  return 0;
}

The output of this program is:

Miles   Metres    Kilometres
0     0     0
10     16090     16
20     32180     32
30     48270     48
40     64360     64
50     80450     80
60     96540     96
70     112630     112
80     128720     128
90     144810     144

As you can see, the output is a little ragged. We'll fix that shortly.

Before we do, though, I hope you have already asked yourself an important question. If printf interprets the "%" symbol as introducing a print field that requires a matching parameter, is there any way to print an actual "%" symbol?

The answer is yes, and it's simply this: "%%". If printf sees a "%" sign immediately followed by another one, it ignores the first one, prints the second one on standard output, and no matching parameter is required or desired. Thus:

  int pea_lovers = 46;
  printf("%d%% of respondents like peas.\n", pea_lovers);

will print:

46% of respondents like peas.

Printing a char

We can print individual characters if we like, using the conversion specifier "c", as follows:

#include <stdio.h>

int main(void)
{
  int input = getchar();
  printf("You typed [%c]\n", input);
  return 0;
}

Note how I used square brackets to mark off the output. You don't have to do this, of course, but it makes the output a little easier to understand when the user types something like a space character or maybe a newline. These square brackets don't mean anything to printf. They are just character data, to be copied to standard output like any other characters.

Printing a string

You can even pass entire strings to printf. To do this, use the conversion specifier s. For example:

#include <stdio.h>

int main(void)
{
  char title[] = "Queen";
  char name[] = "Victoria";
  int accession_year = 1837;

  printf("%s %s acceded in %d\n",
         title,
         name,
         accession_year);
  return 0;
}

Better format control

We can control the width of a print field by placing the width (in print columns, so it's a whole number) between the % and the conversion specifier. Here is our miles-and-metres program again, but this time the output is a lot neater:

#include <stdio.h>

int main(void)
{
  int miles;
  int metres;
  int km;

  puts("Miles Metres Kilometres");

  for(miles = 0; miles < 100; miles += 10)
  {
    metres = miles * 1609;
    km = metres / 1000;
    printf("%5d %6d %5d\n",
           miles, metres, km);
  }
  return 0;
}

Here's our reward:

Miles Metres Kilometres
    0      0     0
   10  16090    16
   20  32180    32
   30  48270    48
   40  64360    64
   50  80450    80
   60  96540    96
   70 112630   112
   80 128720   128
   90 144810   144

As you can see, the widths we specified have been honoured, and leading blanks have been used to pad out shorter numbers to fill the available space.

Summary

In this chapter, you began to learn about the printf function.

In the next chapter, we will discuss C's unsigned integer types.

Progress

Terminology
  • variadic function
  • conversion specifier
  • precedence
  • array
  • index
  • pointer
  • sentinel value
  • null character
  • null terminator
  • string
Syntax
  • comments
  • types
  • char
  • operators
    • increment and decrement operators
      • ++n n++ --n n--
    • assignment operators
      • = += -= *= /= %=
    • additive operators
      • the + operator
      • the - operator
    • multiplicative operators
      • the * operator
      • the / operator
      • the % operator
    • equality and relational operators
      • == != < > <= >= !
    • logical operators
      • the && operator
      • the || operator
    • address and indirection operators
      • the unary * operator
      • the unary & operator
      • the array subscripting operator []
    • miscellaneous operators
      • the conditional operator ? :
      • the comma operator ,
      • the sizeof operator
  • control structures
    • if/else
    • while
    • do/while
    • for
Standard library functions, by header
Personal tools