CTutor chapter 9

                     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

Comments

Popular posts from this blog

BOTTOM LIVE script

Fawlty Towers script for "A Touch of Class"