Category : C Source Code
Archive   : CTUTOR1.ZIP
Filename : CHAP9.TXT

 
Output of file : CHAP9.TXT contained in archive : CTUTOR1.ZIP

Chapter 9 - Standard Input/Output


THE STDIO.H HEADER FILE

Load the file SIMPLEIO.C for our first look at a file

with standard I/O. Standard I/O refers to the most usual

places where data is either read from, the keyboard, or

written to, the video monitor. Since they are used so much,

they are used as the default I/O devices and do not need to

be named in the Input/Output instructions. This will make

more sense when we actually start to use them so lets look

at the file in front of you.

The first thing you will notice is the first line of

the file, the #include "stdio.h" line. This is very much

like the #define we have already studied, except that

instead of a simple substitution, an entire file is read in

at this point. The system will find the file named

"stdio.h" and read its entire contents in, replacing this

statement. Obviously then, the file named "stdio.h" must

contain valid C source statements that can be compiled as

part of a program. This particular file is composed of

several standard #defines to define some of the standard I/O

operations. The file is called a header file and you will

find several different header files on the source disks that

came with your compiler. Each of the header files has a

specific purpose and any or all of them can be included in

any program.

Most C compilers use the double quote marks to indicate

that the "include" file will be found in the current

directory. A few use the "less than" and "greater than"

signs to indicate that the file will be found in a standard

header file. Nearly all MSDOS C compilers use the double


quotes, and most require the "include" file to be in the

default directory. All of the programs in this tutorial

have the double quotes in the "include" statements. If your

compiler uses the other notation, you will have to change

them before compiling.

INPUT/OUTPUT OPERATIONS IN C

Actually the C programming language has no input or

output operations defined as part of the language, they must

be user defined. Since everybody does not want to reinvent

his own input and output operations, the compiler writers

have done a lot of this for us and supplied us with several

input functions and several output functions to aid in our

program development. The functions have become a standard,

and you will find the same functions available in nearly

every compiler. In fact, the industry standard of the C

language definition has become the book written by Kernigan

and Ritchie, and they have included these functions in their



Page 55









Chapter 9 - Standard Input/Output


definition. You will often, when reading literature about

C, find a reference to K & R. This refers to the book

written by Kernigan and Ritchie. You would be advised to

purchase a copy for reference.

You should print out the file named "stdio.h" and spend

some time studying it. There will be a lot that you will

not understand about it, but parts of it will look familiar.

The name "stdio.h" is sort of cryptic for "standard

input/output header", because that is exactly what it does.

It defines the standard input and output functions in the

form of #defines and macros. Don't worry too much about the

details of this now. You can always return to this topic

later for more study if it interests you, but you will

really have no need to completely understand the "stdio.h"

file. You will have a tremendous need to use it however, so

these comments on its use and purpose are necessary.

OTHER INCLUDE FILES

When you begin writing larger programs and splitting

them up into separately compiled portions, you will have

occasion to use some statements common to each of the

portions. It would be to your advantage to make a separate

file containing the statements and use the #include to

insert it into each of the files. If you want to change any

of the common statements, you will only need to change one

file and you will be assured of having all of the common

statements agree. This is getting a little ahead of

ourselves but you now have an idea how the #include

directive can be used.

BACK TO THE FILE NAMED "SIMPLEIO.C"

Lets continue our tour of the file in question. The

one variable "c" is defined and a message is printed out

with the familiar "printf" function. We then find ourselves

in a continuous loop as long as "c" is not equal to capital

X. If there is any question in your mind about the loop

control, you should review chapter 3 before continuing. The

two new functions within the loop are of paramount interest

in this program since they are the new functions. These are

functions to read a character from the keyboard and display

it on the monitor one character at a time.

The function "getchar()" reads a single character from

the standard input device, the keyboard being assumed

because that is the standard input device, and assigns it to

the variable "c". The next function "putchar(c)", uses the

standard output device, the video monitor, and outputs the

character contained in the variable "c". The character is



Page 56









Chapter 9 - Standard Input/Output


output at the current cursor location and the cursor is

advanced one space for the next character. The system is

therefore taking care of a lot of the overhead for us. The

loop continues reading and displaying characters until we

type a capital X which terminates the loop.

Compile and run this program for a few surprises. When

you type on the keyboard, you will notice that what you type

is displayed faithfully on the screen, and when you hit the

return key, the entire line is repeated. In fact, we only

told it to output each character once but it seems to be

saving the characters up and redisplaying them. A short

explanation is in order.

DOS IS HELPING US OUT (OR GETTING IN THE WAY)

We need to understand a little bit about how DOS works

to understand what is happening here. When data is read

from the keyboard, under DOS control, the characters are

stored in a buffer until a carriage return is entered at

which time the entire string of characters is given to the

program. When the characters are being typed, however, the

characters are displayed one at a time on the monitor. This

is called echo, and happens in many of the applications you

run.

With the above paragraph in mind, it should be clear

that when you are typing a line of data into "SIMPLEIO", the

characters are being echoed by DOS, and when you return the

carriage, the characters are given to the program. As each

character is given to the program, it displays it on the

screen resulting in a repeat of the line typed in. To

better illustrate this, type a line with a capital X

somewhere in the middle of the line. You can type as many

characters as you like following the "X" and they will all

display because the characters are being read in under DOS,

echoed to the monitor, and placed in the DOS input buffer.

DOS doesn't think there is anything special about a capital

X. When the string is given to the program, however, the

characters are accepted by the program one at a time and

sent to the monitor one at a time, until a capital X is

encountered. After the capital X is displayed, the loop is

terminated, and the program is terminated. The characters

on the input line following the capital X are not displayed

because the capital X signalled program termination.

Compile and run "SIMPLEIO.C". After running the

program several times and feeling confidant that you

understand the above explanation, we will go on to another

program.




Page 57









Chapter 9 - Standard Input/Output


Don't get discouraged by the above seemingly weird

behavior of the I/O system. It is strange, but there are

other ways to get data into the computer. You will actually

find the above method useful for many applications, and you

will probably find some of the following useful also.

ANOTHER STRANGE I/O METHOD

Load the file named SINGLEIO.C and display it on your

monitor for another method of character I/O. Once again, we

start with the standard I/O header file, we define a

variable named "c", and we print a welcoming message. Like

the last program, we are in a loop that will continue to

execute until we type a capital X, but the action is a

little different here.

The "getch()" is a new function that is a "get

character" function. It differs from "getchar()" in that it

does not get tied up in DOS. It reads the character in

without echo, and puts it directly into the program where it

is operated on immediately. This function then reads a

character, immediately displays it on the screen, and

continues the operation until a capital X is typed.

When you compile and run this program, you will find

that there is no repeat of the lines when you hit a carriage

return, and when you hit the capital X, the program

terminates immediately. No carriage return is needed to get

it to accept the line with the X in it. We do have another

problem here, there is no linefeed with the carriage return.

NOW WE NEED A LINE FEED

It is not apparent to you in most application programs

but when you hit the enter key, the program supplies a

linefeed to go with the carriage return. You need to return

to the left side of the monitor and you also need to drop

down a line. The linefeed is not automatic. We need to

improve our program to do this also. If you will load and

display the program named BETTERIN.C, you will find a change

to incorporate this feature.

In BETTERIN.C, we have two additional statements at the

beginning that will define the character codes for the

linefeed (LF), and the carriage return (CR). If you look at

any ASCII table you will find that the codes 10 and 13 are

exactly as defined here. In the main program, after

outputting the character, we compare it to CR, and if it is

equal to CR, we also output a linefeed which is the LF. We

could have just as well have left out the two #define

statements and used "if (c == 13) putchar(10);" but it would



Page 58









Chapter 9 - Standard Input/Output


not be very descriptive of what we are doing here. The

method used in the program represents better programming

practice.

Compile and run BETTERIN.C to see if it does what we

have said it should do. It should display exactly what you

type in, including a linefeed with each carriage return, and

should stop immediately when you type a capital X.

If you are using a nonstandard compiler, it may not

find a "CR" because your system returns a "LF" character to

indicate end-of-line. It will be up to you to determine

what method your compiler uses. The quickest way is to add

a "printf" statement that prints the input character in

decimal format.

WHICH METHOD IS BEST?

We have examined two methods of reading characters into

a C program, and are faced with a choice of which one we

should use. It really depends on the application because

each method has advantages and disadvantages. Lets take a

look at each.

When using the first method, DOS is actually doing all

of the work for us by storing the characters in an input

buffer and signalling us when a full line has been entered.

We could write a program that, for example, did a lot of

calculations, then went to get some input. While we were

doing the calculations, DOS would be accumulating a line of

characters for us, and they would be there when we were

ready for them. However, we could not read in single

keystrokes because DOS would not report a buffer of

characters to us until it recognized a carriage return.

The second method, used in BETTERIN.C, allows us to get

a single character, and act on it immediately. We do not

have to wait until DOS decides we can have a line of

characters. We cannot do anything else while we are waiting

for a character because we are waiting for the input

keystroke and tying up the entire machine. This method is

useful for highly interactive types of program interfaces.

It is up to you as the programmer to decide which is best

for your needs.

I should mention at this point that there is also an

"ungetch" function that works with the "getch" function. If

you "getch" a character and find that you have gone one too

far, you can "ungetch" it back to the input device. This

simplifies some programs because you don't know that you

don't want the character until you get it. You can only



Page 59









Chapter 9 - Standard Input/Output


"ungetch" one character back to the input device, but that

is sufficient to accomplish the task this function was

designed for. It is difficult to demonstrate this function

in a simple program so its use will be up to you to study

when you need it.

The discussion so far in this chapter, should be a good

indication that, while the C programming language is very

flexible, it does put a lot of responsibility on you as the

programmer to keep many details in mind.

NOW TO READ IN SOME INTEGERS

Load and display the file named INTIN.C for an example

of reading in some formatted data. The structure of this

program is very similar to the last three except that we

define an "int" type variable and loop until the variable

somehow acquires the value of 100.

Instead of reading in a character at a time, as we have

in the last three files, we read in an entire integer value

with one call using the function named "scanf". This

function is very similar to the "printf" that you have been

using for quite some time by now except that it is used for

input instead of output. Examine the line with the "scanf"

and you will notice that it does not ask for the variable

"valin" directly, but gives the address of the variable

since it expects to have a value returned from the function.

Recall that a function must have the address of a variable

in order to return the value to the calling program.

Failing to supply a pointer in the "scanf" function is

probably the most common problem encountered in using this

function.

The function "scanf" scans the input line until it

finds the first data field. It ignores leading blanks and

in this case, it reads integer characters until it finds a

blank or an invalid decimal character, at which time it

stops reading and returns the value.

Remembering our discussion above about the way the DOS

input buffer works, it should be clear that nothing is

actually acted on until a complete line is entered and it is

terminated by a carriage return. At this time, the buffer

is input, and our program will search across the line

reading all integer values it can find until the line is

completely scanned. This is because we are in a loop and we

tell it to find a value, print it, find another, print it,

etc. If you enter several values on one line, it will read

each one in succession and display the values. Entering the

value of 100 will cause the program to terminate, and



Page 60









Chapter 9 - Standard Input/Output


entering the value 100 with other values following, will

cause termination before the following values are

considered.

IT MAKES WRONG ANSWERS SOMETIMES

If you enter a number up to and including 32767, it

will display correctly, but if you enter a larger number, it

will appear to make an error. For example, if you enter the

value 32768, it will display the value of -32768, entering

the value 65536 will display as a zero. These are not

errors but are caused by the way an integer is defined. The

most significant bit of the 16 bit pattern available for the

integer variable is the sign bit, so there are only 15 bits

left for the value. The variable can therefore only have

the values from -32768 to 32767, any other values are

outside the range of integer variables. This is up to you

to take care of in your programs. It is another example of

the increased responsibility you must assume using C rather

than a higher level language such as Pascal, Modula-2, etc.

The above paragraph is true for most MS-DOS C

compilers. There is a very small possibility that your

compiler uses an integer value other than 16 bits. If that

is the case, the same principles will be true but at

different limits than those given above.

Compile and run this program, entering several numbers

on a line to see the results, and with varying numbers of

blanks between the numbers. Try entering numbers that are

too big to see what happens, and finally enter some invalid

characters to see what the system does with nondecimal

characters.

CHARACTER STRING INPUT

Load and display the file named STRINGIN.C for an

example of reading a string variable. This program is

identical to the last one except that instead of an integer

variable, we have defined a string variable with an upper

limit of 24 characters (remember that a string variable must

have a null character at the end). The variable in the

"scanf" does not need an & because "big" is an array

variable and by definition it is already a pointer. This

program should require no additional explanation. Compile

and run it to see if it works the way you expect.

You probably got a surprise when you ran it because it

separated your sentence into separate words. When used in

the string mode of input, "scanf" reads characters into the

string until it comes to either the end of a line or a blank



Page 61









Chapter 9 - Standard Input/Output


character. Therefore, it reads a word, finds the blank

following it, and displays the result. Since we are in a

loop, this program continues to read words until it exhausts

the DOS input buffer. We have written this program to stop

whenever it finds a capital X in column 1, but since the

sentence is split up into individual words, it will stop

anytime a word begins with capital X. Try entering a 5 word

sentence with a capital X as the first character in the

third word. You should get the first three words displayed,

and the last two simply ignored when the program stops.

Try entering more than 24 characters to see what the

program does. It should generate an error, but that will be

highly dependent on the system you are using. In an actual

program, it is your responsibility to count characters and

stop when the input buffer is full. You may be getting the

feeling that a lot of responsibility is placed on you when

writing in C. It is, but you also get a lot of flexibility

in the bargain too.

INPUT/OUTPUT PROGRAMMING IN C

C was not designed to be used as a language for lots of

input and output, but as a systems language where a lot of

internal operations are required. You would do well to use

another language for I/O intensive programming, but C could

be used if you desire. The keyboard input is very flexible,

allowing you to get at the data in a very low level way, but

very little help is given you. It is therefore up to you to

take care of all of the bookkeeping chores associated with

your required I/O operations. This may seem like a real

pain in the neck, but in any given program, you only need to

define your input routines once and then use them as needed.

Don't let this worry you. As you gain experience with

C, you will easily handle your I/O requirements.

One final point must be made about these I/O functions.

It is perfectly permissible to intermix "scanf" and

"getchar" functions during read operations. In the same

manner, it is also fine to intermix the output functions,

"printf" and "putchar".

IN MEMORY I/O

The next operation may seem a little strange at first,

but you will probably see lots of uses for it as you gain

experience. Load the file named INMEM.C and display it for

another type of I/O, one that never accesses the outside

world, but stays in the computer.




Page 62









Chapter 9 - Standard Input/Output


In INMEM.C, we define a few variables, then assign some

values to the ones named "numbers" for illustrative purposes

and then use a "sprintf" function. The function acts just

like a normal "printf" function except that instead of

printing the line of output to a device, it prints the line

of formatted output to a character string in memory. In

this case the string goes to the string variable "line",

because that is the string name we inserted as the first

argument in the "sprintf" function. The spaces after the

2nd %d were put there to illustrate that the next function

will search properly across the line. We print the

resulting string and find that the output is identical to

what it would have been by using a "printf" instead of the

"sprintf" in the first place. You will see that when you

compile and run the program shortly.

Since the generated string is still in memory, we can

now read it with the function "sscanf". We tell the

function in its first argument that "line" is the string to

use for its input, and the remaining parts of the line are

exactly what we would use if we were going to use the

"scanf" function and read data from outside the computer.

Note that it is essential that we use pointers to the data

because we want to return data from a function. Just to

illustrate that there are many ways to declare a pointer

several methods are used, but all are pointers. The first

two simply declare the address of the elements of the array,

while the last three use the fact that "result", without the

accompanying subscript, is a pointer. Just to keep it

interesting, the values are read back in reverse order.

Finally the values are displayed on the monitor.

IS THAT REALLY USEFUL?

It seems sort of silly to read input data from within

the computer but it does have a real purpose. It is

possible to read data in using any of the standard functions

and then do a format conversion in memory. You could read

in a line of data, look at a few significant characters,

then use these formatted input routines to reduce the line

of data to internal representation. That would sure beat

writing your own data formatting routines.

STANDARD ERROR OUTPUT

Sometimes it is desirable to redirect the output from

the standard output device to a file. However, you may

still want the error messages to go to the standard output

device, in our case the monitor. This next function allows

you to do that. Load and display SPECIAL.C for an example of

this new function.



Page 63









Chapter 9 - Standard Input/Output



The program consists of a loop with two messages

output, one to the standard output device and the other to

the standard error device. The message to the standard

error device is output with the function "fprintf" and

includes the device name "stderr" as the first argument.

Other than those two small changes, it is the same as our

standard "printf" function. (You will see more of the

"fprintf" function in the next chapter, but its operation

fit in better as a part of this chapter.) Ignore the line

with the "exit" for the moment, we will return to it.

Compile and run this program, and you will find 12

lines of output on the monitor. To see the difference, run

the program again with redirected output to a file named

"STUFF" by entering the following line at the Dos prompt;

A> special >stuff

More information about I/O redirection can be found in

your DOS manual. This time you will only get the 6 lines

output to the standard error device, and if you look in your

directory, you will find the file named "STUFF" containing

the other 6 lines, those to the standard output device. You

can use I/O redirection with any of the programs we have run

so far, and as you may guess, you can also read from a file

using I/O redirection but we will study a better way to read

from a file in the next chapter.

WHAT ABOUT THE exit(4) STATEMENT?

Now to keep our promise about the exit(4) statement.

Redisplay the file named SPECIAL.C on your monitor. The

last statement simply exits the program and returns the

value of 4 to DOS. Any number from 0 to 9 can be used in

the parentheses for DOS communication. If you are operating

in a BATCH file, this number can be tested with the

"ERRORLEVEL" command.

Most compilers that operate in several passes return a

1 with this mechanism to indicate that a fatal error has

occurred and it would be a waste of time to go on to another

pass resulting in even more errors.

It is therefore wise to use a batch file for compiling

programs and testing the returned value for errors. A check

of the documentation for my COMPAQ, resulted in a minimal

and confusing documentation of the "errorlevel" command, so

a brief description of it is given in this file.





Page 64









Chapter 9 - Standard Input/Output


PROGRAMMING EXERCISE

1. Write a program to read in a character using a loop,

and display the character in its normal "char" form.

Also display it as a decimal number. Check for a

dollar sign to use as the stop character. Use the

"getch" form of input so it will print immediately. Hit

some of the special keys, such as function keys, when

you run the program for some surprises. You will get

two inputs from the special keys, the first being a

zero which is the indication to the system that a

special key was hit.









































Page 65