C for Yourself - part 11

In this month's tutorial, Steve Mumford investigates the functions associated with input and output.

Over the past few columns, we've had a look at some of the basics associated with creating a C program. However, we haven't paid much attention to the actual mechanics of passing information back and forth between the computer and the various devices attached to it. Not only is this vital for communicating with the user by means of the keyboard and VDU, but it also includes the saving and loading of files from disc.

In order to demonstrate the importance of these functions, cast your minds back a couple of months - David introduced the concept of arrays and used them to demonstrate the implementation of a simple database. Now imagine that database without loading or saving facilities, then remove all of the text displayed on the screen. Pretty grim, isn't it?

In this article I'll begin to take a look at the functions that cover input and output, starting off with printf() and scanf(). I'll then mention some of the processes involved in file handling, and we'll look at some of the more subtle commands next month.

Printing to the screen

If you take a look at the source for a few C programs, you'll notice that the great majority of them include the line:

#include <stdio.h>

This orders the compiler to include the header file stdio.h which provides links to the standard routines used for input and output. They may not be the most elegant functions ever designed, but what's important is that they are standard on all versions of C - if you ever want your code to be easily portable, these commands are what you need.

The printf() function allows you to format text in some way as you print it - that's what the 'f' is there for. The format of the printf() command is shown below:

printf("This is some text\n");

So far, so good. The above command prints the phrase to the standard output - in normal cases this sends the message to the screen. The text between the quotation marks is termed the control string, and defines how your text is to appear on the screen. The \n code represents a newline character and is necessary because the printf() function does not automatically add one on to the end of a statement - unlike the PRINT command in BASIC. It's possible to include the codes anywhere in the control string, and this provides an easy way of printing more than one line at once.

printf("First line\nSecond line\n\nFourth line\n");

However, if you want to print out the value of a variable, the format of the command alters slightly. Consider the following:

int num = 14;
printf("The value of num = %d\n", num);

In this example, the computer would print the phrase in the quotation marks, replaceing the 'conversion specifier' %d with the integer value of num. It's important to keep track of your variables - if num isn't an integer and you attempt to print it out as one, things would go sadly wrong. If you have more than one variable to print out, you'll need the corresponding number of specifiers in the control string:

printf("var1 = %d, var2 = %d\n", var1, var2);

It's possible to add more information to the conversion specifiers which allow you to control the justification and length of the values you're printing. I've included several examples of printf() statements on the cover disc.

Reading from the keyboard

The routine to complement printf() is called scanf() and is a little different in the way it works. Although it looks very similar to the format of the printf() statement, it's important to understand that one is not the precise opposite of the other. Here's an example:

int var = 0;
printf("Please enter an integer:\n");
scanf("%d", &var);

You've still got the control string telling the command what it's looking for, but instead of a list of variables following it, there's a list of pointers. The scanf() function is looking for somewhere to store the data, and that's why it needs an address in the form of a pointer instead of a simple variable name. You can dispense with the ampersand if you're actually giving scanf() a pointer to begin with:

int *var;
scanf("%d", var);

In fact, if you did write &var the compiler would probably stop speaking to you. As you will discover, scanf() is somewhat fussy - that's why I had to include those special loops in last month's example programs.

scanf("%d", &choice);
while (getchar() != '\n') { }

The scanf() function looks for an integer entered from the keyboard when it is called. If it can't find one because you've just typed in some text, it gets upset - instead of giving up and wiping the input stream, which would initialise it for the next input, it leaves the offending data where it was. What this does, in effect, is to clog the input buffer, and any following scanf() statements are very likely to fail, since they're receiving data they weren't expecting.

This is where the while loop comes in. After the scanf() statement has executed, this loop steps through and discards whatever's left in the input buffer using the getchar() function until it finds a newline code. Since any data you enter from the keyboard ends with a newline, this neat loop purges the buffer and readies it for any following input. If you're going to be using scanf(), it makes sense to include these housekeeping devices - it's quite infuriating to see your masterpiece of coding curl up simply because you typed 'O' instead of '0'. I've got the tee-shirt, as they say.

File handling

To round off this month's tutorial, I'll take a quick look at how you redirect output to a file. The functions used to perform these operations are very similar to the ones we've used above - they are fprintf() and fscanf(). Their similarity arises from the fact that C attempts to treat all its inputs and outputs in the same manner, so whether it's printing to the screen or saving to a file, the overall mechanism is still the same.

There are two extra functions you must know about before you can use files to store data - these are fopen() and fclose(). Before saving data the computer must have been given some warning so it knows where to store the information. Conversely, when you've stopped using a file you must close it so that all the updates are made permanent and things are tidied up. When you open a file, you are given a handle - a form of identity number for that open file. From then on, instead of referring to the file by its name, you use the handle:

FILE *handle;
if ((handle = fopen("testfile", "w")) == 0)
{
  printf("Test file could not be opened.\n");
}

The variable type FILE is defined in stdio.h, and contains information the computer needs to reference a data file - we need to create a pointer to a variable of that type. If the file could not be opened, then fopen() returns a value of zero, and this should be checked by the program so it can recover gracefully if there's a problem with the disc.

The fopen() statement takes two textual parameters - the first is the filename and the second determines what sort of access is needed on the file. In the above example, the filename is testfile and its access is w, which tells the computer to open a file for write access only. If the file does not exist, this command will create one. There are several methods of access, and as well as read and write there's also append which writes new data to the end of an old file. There's also a binary mode in which numbers are stored in machine representation rather than being converted to text.

Once you've finished with a file, you must close it with fclose():

if (fclose(handle) != 0)
{
  printf("File couldn't be closed\n");
}

If the file is closed properly the fclose() function will return a value of zero - again, it's important that this is checked.

Well, we're able to open and close files at will, but we haven't actually saved anything yet. I'll cover this next month, along with some more of the input and output routines.


Source: Acorn User - 155 - May 1995
Publication: Acorn User
Contributor: Steve Mumford