C Tutorial

C Tutorial
為了增進各會員的電腦技巧, 本人已完成  Post 一套 C 的 Tutorial 給各會員, 雖然內容為全英文, 但作者以循序漸進方式來編寫此 Tutorial, 而且每個 Topic 都有
examples, 所以很易上手.

我已 upload 完 C tutorial, 稍後會 upload  C++ tutorial.

另外, Borland 已於網上發放一個 C compiler (可 compile C 及 C++ program 成為 EXE file), 若會員沒有 C compiler 可以上網 download:

www.happyfunnyland.com/download/TC.zip

C tutorial Chapter 14 - Example Programs [End]
Chapter 14 - Example Programs


                             WHY THIS CHAPTER?

             Although  every  program  in this tutorial has  been  a
        complete  program,  each  one  has also been  a  very  small
        program intended to teach you some principle of  programming
        in  C.   It  would do you a disservice to leave you at  that
        point  without introducing you to a few larger  programs  to
        illustrate  how  to  put together the  constructs  you  have
        learned  to create a major program.   This chapter  contains
        four  programs  of increasing complexity,  each designed  to
        take  you  into a higher plateau of  programming,  and  each
        designed to be useful to you in some way.

             DOSEX will illustrate how to make DOS system calls  and
        will teach you,  through self-study, how the system responds
        to  the  keyboard.   WHATNEXT  reads commands input  on  the
        command line and will aid you in setting up a variable batch
        file,  one  that requests an operator input and responds  to
        the  input  by branching to a different part  of  the  batch
        file.

             LIST  is  the source code for the program you  used  to
        print  out the C source files when you began studying C with
        the aid of this tutorial.  Finally we come to VC, the Visual
        Calculator,  which  you should find to be a  useful  program
        even  if you don't study its source code.   VC uses most  of
        the  programming  techniques we have studied in this  course
        and  a few that we never even mentioned such  as  separately
        compiled subroutines.

             We  will  take a look at the example programs one at  a
        time  but  without  a complete explanation of  any  of  them
        because  you  have  been studying C for some  time  now  and
        should be able to read and understand most of these programs
        on  your  own.

/******************************************************************/
/* This is an example program to illustrate how to;               */
/*  1. Get the time and date from DOS                             */
/*  2. Set the cursor to any position on the screen               */
/*  3. Read characters from the keyboard and display their codes  */
/*  4. How to scroll a window up on the monitor                   */
/*  5. Format a program for ease of reading and understanding     */
/*  6. How to do proper prototyping                               */
/******************************************************************/

void draw_box(void);
void disp_char(int inchar);
void get_time(int *hour,int *minute,int *second);
void disp_time_date(void);
void pos_cursor(char row,char column);
void scroll_window(void);

#include "stdio.h"
#include "dos.h"
#include "conio.h"

int main()
{
int hour, minute, sec, old_sec;
int character;

   draw_box();              /* draw the boxes around the fields */
   old_sec = 0;             /* this variable stores the old time
                               so we can look for a change      */

   do {
      if (kbhit()) {                     /* has a key been hit? */
         character = getch();            /* read it in          */
         disp_char(character);           /* display it          */
      }

      get_time(&hour,&minute,&sec);      /* get the time of day */
      if (sec != old_sec) {              /* if it has changed,  */
         disp_time_date();               /* update the display  */
         old_sec = sec;                  /* save new time       */
      }

   } while (character != 'Q');        /* Quit when a Q is found */

   pos_cursor(0,0);              /* put cursor at top of screen */
}


/* **************************************************** drawbox */
/* This routine draws a box on the screen. The keys hit, and    */
/* the time and date are displayed in these boxes. There is     */
/* nothing special about these boxes, they are simply output    */
/* using the printf function.                                   */
/* ************************************************************ */
void draw_box(void)
{
int index;
char line[81];

   for (index = 0;index < 80;index++)       /* three blank rows */
      line[index] = ' ';
   line[80] = NULL;                            /* end of string */
   for (index = 0;index < 3;index++)
      printf("%s",line);

   line[8] = 201;                       /* draw top line of box */
   for (index = 9;index < 70;index++)
      line[index] = 205;
   line[70] = 187;
   printf("%s",line);

   line[8] = 186;                    /* draw sides of large box */
   for (index = 9;index < 70;index++)
      line[index] = ' ';
   line[70] = 186;
   for (index = 0;index < 15;index++)
      printf("%s",line);

   line[8] = 204;                    /* draw line between boxes */
   for (index = 9;index < 70;index++)
      line[index] = 205;
   line[70] = 185;
   printf("%s",line);

   line[8] = 186;                    /* sides for time/date box */
   for (index = 9;index < 70;index++)
      line[index] = ' ';
   line[70] = 186;
   printf("%s",line);

   line[8] = 200;                     /* bottom line of the box */
   for (index = 9;index < 70;index++)
      line[index] = 205;
   line[70] = 188;
   printf("%s",line);

   for (index = 0;index < 80;index++)       /* three blank rows */
      line[index] = ' ';
   for (index = 0;index < 3;index++)
      printf("%s",line);

}


/* ************************************************** disp_char */
/* This routine displays the characters hit on the monitor. If  */
/* the first character is a zero, a special character has been  */
/* hit, and the zero is displayed. The next character is read,  */
/* and it is displayed on the monitor.                          */
/* ************************************************************ */
void disp_char(int inchar)
{
   scroll_window();
   pos_cursor(17,15);          /* position of message on screen */

   if(inchar == 0) {
      printf(" 00 ");            /* a special character was hit */
      inchar = getch();          /* get the next part of it     */
      switch (inchar) {
         case 59  :
         case 60  :
         case 61  :
         case 62  :
         case 63  :              /* these are the function keys */
         case 64  :
         case 65  :
         case 66  :
         case 67  :
         case 68  : printf("%4d Function key F%d\n",inchar,inchar-58);
                    break;

         case 94  :
         case 95  :
         case 96  :
         case 97  :
         case 98  :         /* these are the ctrl-function keys */
         case 99  :
         case 100 :
         case 101 :
         case 102 :
         case 103 : printf("%4d Function key Ctrl-F%d\n",inchar,
                       inchar-93);
                    break;

         case 84  :
         case 85  :
         case 86  :
         case 87  :        /* these are the upper-function keys */
         case 88  :
         case 89  :
         case 90  :
         case 91  :
         case 92  :
         case 93  : printf("%4d Function key Upper-F%d\n",inchar,
                       inchar-83);
                    break;

         case 104 :
         case 105 :
         case 106 :
         case 107 :
         case 108 :          /* these are the alt-function keys */
         case 109 :
         case 110 :
         case 111 :
         case 112 :
         case 113 : printf("%4d Function key Alt-F%d\n",inchar,
                       inchar-103);
                    break;

         default  : printf("%4d Special key hit\n",inchar);
      }

   } else                        /* a regular character was hit */
      printf("    %4d (%c) Character Hit.\n",inchar,inchar);

   pos_cursor(25,1);        /* hide the cursor on the 26th line */
}


/* *************************************************** get_time */
/* This routine calls the DOS function call for time of day. It */
/* returns the time of day to the calling program in the three  */
/* pointers used in the call.                                   */
/* ************************************************************ */
void get_time(int *hour,int *minute,int *second)
{
union REGS inregs;
union REGS outregs;

   inregs.h.ah = 44;               /* Hex 2C - Get current time */
   int86(0x21,&inregs,&outregs);
   *hour = outregs.h.ch;
   *minute = outregs.h.cl;
   *second = outregs.h.dh;
}


/* ********************************************* disp_time_date */
/* This routine displays the time and date on the monitor in a  */
/* fixed position. It gets the time from the get_time function, */
/* and gets the date from its own built in DOS call. Good       */
/* programming practice would move the date to another function */
/* but this is an illustrative example to display methods of    */
/* doing things. This routine also calls the cursor positioning */
/* function to put the time and date where we want them.        */
/* ************************************************************ */
void disp_time_date(void)
{
int hour, minute, second;
union REGS inregs;
union REGS outregs;

   pos_cursor(19,19);  /* position the cursor for date and time */

   inregs.h.ah = 42;              /* hex 2A - What is the date? */
   int86(0x21,&inregs,&outregs);  /* interrupt 21               */
   printf("Date = %2d/%2d/%2d    ",
      outregs.h.dh,                 /* month - 1 to 12          */
      outregs.h.dl,                 /* day - 1 to 31            */
      outregs.x.cx);                /* year - 1980 to 2099      */

   get_time(&hour, &minute, &second);
   printf("Time = %2d:%2d:%2d\n",hour, minute, second);

   pos_cursor(25,1);        /* hide the cursor on the 26th line */
}

/* ************************************************* pos_cursor */
/* This routine positions the cursor at the requested row and   */
/* column. The upper left corner is row 0 and column 0          */
/* ************************************************************ */
void pos_cursor(char row,char column)
{
union REGS inregs;
union REGS outregs;

   inregs.h.ah = 2;        /* service 2 - position the cursor   */
   inregs.h.dh = row;
   inregs.h.dl = column;
   inregs.h.bh = 0;
   int86(0x10,&inregs,&outregs);                /* interrupt 10 */
}


/* ********************************************** scroll_window */
/* This routine scrolls all of the material in the key hit      */
/* window up one space leaving room for another entry.          */
/* ************************************************************ */
void scroll_window(void)
{
union REGS inregs;
union REGS outregs;

   inregs.h.ah = 6;      /* service 6 - scroll window           */
   inregs.h.al = 1;      /* number of lines to scroll           */
   inregs.h.ch = 3;      /* top row of window                   */
   inregs.h.cl = 9;      /* left column of window               */
   inregs.h.dh = 17;     /* bottom row of window                */
   inregs.h.dl = 69;     /* right column of window              */
   inregs.h.bh = 7;      /* attribute of blank line             */
   int86(0x10,&inregs,&outregs);                /* interrupt 10 */

}
        
        
                     DOSEX.C - The DOS Example Program

             The  copy of DOS that you received with your IBM-PC  or
        compatible has about 80 internal DOS calls that you can  use
        as  a programmer to control your peripheral devices and read
        information  or status from them.   Some of the earlier  IBM
        DOS manuals, DOS 2.0 and earlier, have these calls listed in
        the back of the manual along with how to use them.   Most of
        the  manuals  supplied  with compatible  computers  make  no
        mention  of  these  calls even  though  they  are  extremely
        useful.   These  calls  can  be  accessed  from  nearly  any
        programming  language but they do require some initial study
        to learn how to use them.   This program is intended to  aid
        you in this study.



                                  Page 99









                     Chapter 14 - Example Programs


             Display the program on your monitor or print it out for
        reference.   It  is  merely a loop watching for  a  keyboard
        input or a change in the time.  If either happens, it reacts
        accordingly.   In line 32,  the function "kbhit()" returns a
        value  of 1 if a key has been hit but not yet read from  the
        input buffer by the program.

             Look at the function named "get_time" for an example of
        a  DOS call.   An interrupt 21(hex) is called after  setting
        the  AH  register to 2C(hex) =  44(decimal).   The  time  is
        returned in the CH,  CL, and DH registers.  Refer to the DOS
        call  definitions in your copy of DOS.   If the  definitions
        are  not included there,  Peter Nortons  book,  "rogrammers
        Guide  to  the  IBM PC" is recommended as a  good  reference
        manual   for   these  calls  and  many   other   programming
        techniques.   Your compiler may have a built in function  to
        do this.  If you read your documentation, you will  probably
        find many useful functions available with your compiler that
        are  included  as  a convenience for you  by  your  compiler
        writer.

             Another useful function is the "pos_cursor()"  function
        that  positions the cursor anywhere on the monitor that  you
        desire  by  using  a  DOS  interrupt.   In  this  case,  the
        interrupt  used  is  10(hex) which is  the  general  monitor
        interrupt.  This particular service is number 2 of about  10
        different  monitor  services available.   This  function  is
        included here as another example to you.

             The  next  function,  service  number  6  of  interrupt
        10(hex)  is the window scroll service.   It should  be  self
        explanatory.

             In this program, the cursor is positioned and some data
        is  output  to the monitor,  then the cursor is "hidden"  by
        moving  it  to line 26 which is not  displayed.   After  you
        compile and run the program, you will notice that the cursor
        is  not  visible on the monitor.   This is possible  in  any
        program,  but  be  sure  to put the cursor  in  view  before
        returning  to  DOS  because  DOS does not  like  to  have  a
        "hidden" cursor and may do some strange things.

             Some time spent studying this program will be  valuable
        to  you as it will reveal how the keyboard data is input  to
        the  computer.   Especially of importance is how the special
        keys such as function keys, arrows, etc. are handled.   Also
        note that this program uses full prototype checking and is a
        good  example  of  how to use it.  Since it  also  uses  the
        "modern"  method  of  function definitions,  it  is  a  good
        example of that also.



                                  Page 100









                     Chapter 14 - Example Programs


/* *************************************************************** */
/* This program reads a series of words from the command line,     */
/* and displays all but the last on the monitor. The last is a     */
/* series of characters which are used as input comparisons. One   */
/* character is read from the keyboard. If it is one of the        */
/* characters in the comparison list, its number is returned to    */
/* DOS as the errorlevel command. If the character does not exist  */
/* in the list, a zero is returned. Example follows;               */
/*                                                                 */
/* WHATNEXT What model do you want? ALR%3T                         */
/*                                                                 */
/* What model do you want?      <---- displayed on monitor         */
/*    If key a or A is hit, errorlevel 1 is returned.              */
/*    If key l or L is hit, errorlevel 2 is returned.              */
/*    If key r or R is hit, errorlevel 3 is returned.              */
/*    If key % is hit, errorlevel 4 is returned.                   */
/*    If key 3 is hit, errorlevel 5 is returned.                   */
/*    If key t or T is hit, errorlevel 6 is returned.              */
/*    If any other key is hit, errorlevel 0 is returned.           */
/*                                                                 */
/*  The question must be on one line.                              */
/*  Up to nine different keys can be used.                         */
/*  The errorlevel can be interpreted in a batchfile.              */
/* *************************************************************** */

#include "stdio.h"
#include "ctype.h"
#include "conio.h"
#include "process.h"

main(int number,char *name[])
{
int index;                  /* a counter and incrementing variable */
int c;                     /* the character read in for comparison */
int code;                  /* the resulting errorlevel returned to */
char next_char;                    /* used for the comparison loop */
char *point;               /* a dummy pointer used for convenience */

                     /* At least one group must be used for this   */
                     /* filename, and one group used for the       */
                     /* required fields, so less than three allows */
                     /* for no question.                           */
   if (number < 3) {
      printf("No question given on command line\n");
      exit(0);
   }

                         /* print out words 2 to n-1, the question */
   number--;
   for(index = 1;index < number;index++) {
      printf("%s ",name[index]);
   }

                   /* get the users response and make it uppercase */
   c = getch();
   printf("%c\n",c);
   if (islower(c))
      c = toupper(c);
   point = name[number];/* point to the last pointer on the inputs */

   code = 0;
   index = 0;
   do {            /* search across allowed responses in last word */
      next_char = *(point + index);
      if (islower(next_char))
         next_char = toupper(next_char);      /* make it uppercase */
      if(next_char == c)                    /* if a match is found */
         code = index + 1;         /* save the number of the match */
      index++;
   } while (*(point + index));      /* until NULL terminator found */

   exit(code);               /* return the errorcode to the system */
}

                  WHATNEXT.C - The Batch File Interrogator

             This  is  an  example of how to read the  data  on  the
        command line following the function call.  Notice that there
        are  two variables listed within the  parentheses  following
        the main() call.   The first variable is a count of words in
        the entire command line including the command itself and the
        second  variable  is  a  pointer to  an  array  of  pointers
        defining the actual words on the command line.

             First the question on the command line, made up of some
        number of words, is displayed on the monitor and the program
        waits for the operator to hit a key.   If the key hit is one
        of  those  in the last "word" of the group of words  on  the
        command  line,  the number of the character within the group
        is  returned to the program where it can be tested with  the
        "errorlevel" command in the batch file.   You could use this
        technique  to  create a variable AUTOEXEC.BAT  file  or  any
        other  batch  file  can  use this for  a  many  way  branch.
        Compile  and  run this file with TEST.BAT for an example  of
        how  it  works in practice.   You may  find  this  technique
        useful  in  one  of  your batch files and  you  will  almost
        certainly  need  to  read in  the  command  line  parameters
        someday.

             An  interesting alternative would be for you to write a
        program  named "WOULD.C" that would return a 1 if a  "Y"  or
        "y"  were typed and a zero if any other key were hit.   Then
        your batch file could have a line such as;

        WOULD YOU LIKE TO USE THE ALTERNATIVE METHOD (Y/N)

             Dos would use "WOULD" as the program name,  ignore  the
        rest  of  the  statement  except for displaying  it  on  the
        screen.   You  would  then respond to the  question  on  the
        monitor  with a single keyhit.   Your batch file would  then
        respond   to  the  1  or  0  returned  and  either  run  the
        alternative  part  of  the batch file or  the  primary  part
        whatever each part was.

        WOULD YOU LIKE PRIMARY (Y/N)
        IF ERRORLEVEL 1 GOTO PRIMARY
        (secondary commands)
        GOTO DONE
        RIMARY
        (primary commands)
        ONE





                                  Page 101









                     Chapter 14 - Example Programs


/* *************************************************************** */
/* This program will read in any text file and list it on the      */
/* monitor with line numbers and with page numbers.                */
/* *************************************************************** */

#include "stdio.h"                     /* standard I/O header file */
#include "io.h"                        /* file I/O prototypes      */

void open_file(int no,char *name);
void open_print_file(void);
void print_a_line(void);
void top_of_page(void);

#define MAXCHARS 255                     /* maximum size of a line */
FILE *file_point;                    /* pointer to file to be read */
FILE *print_file_point;                      /* pointer to pronter */
char oneline[256];                     /* input string buffer area */

main(number,name)
int number;                 /* number of arguments on command line */
char *name[];               /* arguments on the command line       */
{
char *c;                       /* variable to indicate end of file */
char *point;

   point = name[1];
   open_file(number,point);     /* open the file to read and print */
   open_print_file();

   do {
      c = fgets(oneline,MAXCHARS,file_point);     /* read one line */
      if (c != NULL)
         print_a_line();                         /* print the line */
   } while (c != NULL);                      /* continue until EOF */

   top_of_page();                     /* move paper to top of page */
   fclose(file_point);                           /* close read file */
   fclose(print_file_point);                  /* close printer file */
}

                     
                        LIST.C - The Program Lister

             This program is actually composed of two files,  LIST.C
        and  LISTF.C  that must be separately  compiled  and  linked
        together  with your linker.   There is nothing new here  and
        you  should  have  no  trouble compiling  and  linking  this
        program  by  reading the documentation  supplied  with  your
        C compiler.

             The  only  thing  that is new in this  program  is  the
        inclusion   of  three  "extern"  variables  in  the  LISTF.C
        listing.   The only purpose for this is to tie these  global
        variables  to  the main program and tell the  compiler  that
        these  are not new variables.   The compiler will  therefore
        not  generate any new storage space for them but simply  use
        their names during the compile process.   At link time,  the
        linker  will  get  their actual storage locations  from  the
        LIST.OBJ  file and use those locations for the variables  in
        the  LISTF part of the memory map also.   The  variables  of
        those  names in both files are therefore the same  identical
        variables and can be used just as any other global variables
        could be used if both parts of the program were in one file.

        /* *************************************************************** */
/* This module contains the functions called by the list.c program */
/* program. If this were a program to be used for some specific    */
/* purpose, it would probablly not be wise to break it up into two */
/* separately compiled modules. It is only done here for purposes  */
/* of illustration. It is a useful program.                        */
/* *************************************************************** */

#define MAXLINES 54            /* maximum number of lines per page */
#include "stdio.h"                     /* standard I/O header file */
#include "string.h"                    /* prototypes for strings   */

void open_file(int no,char *name);
void open_print_file(void);
void print_a_line(void);
void header(void);
void top_of_page(void);

extern FILE *file_point;         /* pointer to the file to be read */
extern FILE *print_file_point;           /* pointer to the printer */
extern char oneline[];                 /* input string buffer area */
char filename[15];               /* filename from header or prompt */
int line_number = 0;             /* line number initialized to one */
int page_number = 1;             /* page number initialized to one */
int lines_this_page = 0;              /* lines on this page so far */


/* ***************************************************** open_file */
/* This function opens the input file named on the command line,   */
/* if there was one defined. Otherwise, it requests a file name to */
/* open and opens the requested file.                              */
/* *************************************************************** */
void open_file(int no,char *name)
{

   strcpy(filename,name);         /* copy name for printing header */
   file_point = NULL;           /* if no name was given in command */
   if (no == 2) {              /* 2nd field in command is filename */
      file_point = fopen(name,"r");         /* open requested file */
      if (file_point == NULL)        /* NULL if file doesn't exist */
         printf("Filename on command line doesn't exist!\n");
   }

   do {
      if (file_point == NULL) {                 /* no filename yet */
         printf("Enter filename -> ");
         scanf("%s",filename);
         file_point = fopen(filename,"r");            /* open file */
         if (file_point == NULL)          /* NULL if file no exist */
            printf("Filename doesn't exist, try again.\n");
      }
   } while (file_point == NULL);   /* continue until good filename */
}


/* *********************************************** open_print_file */
/* This function opens the printer file to the standard printer.   */
/* *************************************************************** */
void open_print_file(void)
{
   print_file_point = fopen("RN","w");       /* open printer file */
}


/* ************************************************** print_a_line */
/* This routine prints a line of text and checks to see if there   */
/* is room for another line on the page. If not, it starts a new   */
/* page with a new header. This routine calls several other local  */
/* routines.                                                       */
/* *************************************************************** */
void print_a_line(void)
{
int index;

   header();
   printf("%5d %s",line_number,oneline);

                       /* This prints a line of less than 72 chars */
   if (strlen(oneline) < 72)
      fprintf(print_file_point,"%5d %s",line_number,oneline);

                          /* This prints a line of 72 to 143 chars */
   else if (strlen(oneline) < 144) {
      fprintf(print_file_point,"%5d ",line_number);
      for (index = 0;index < 72;index++)
         fprintf(print_file_point,"%c",oneline[index]);
      fprintf(print_file_point,"<\n      ");
      for (index = 72;index < strlen(oneline);index++)
         fprintf(print_file_point,"%c",oneline[index]);
      lines_this_page++;
   }

                          /* This prints a line of 144 to 235 chars */
   else if (strlen(oneline) < 235) {
      fprintf(print_file_point,"%5d ",line_number);
      for (index = 0;index < 72;index++)
         fprintf(print_file_point,"%c",oneline[index]);
      fprintf(print_file_point,"<\n      ");
      for (index = 72;index < 144;index++)
         fprintf(print_file_point,"%c",oneline[index]);
      fprintf(print_file_point,"<\n      ");
      for (index = 144;index < strlen(oneline);index++)
         fprintf(print_file_point,"%c",oneline[index]);
      lines_this_page += 2;
   }
         /* the following line outputs a newline if there is none
                                      at the end of the last line */
   if (oneline[strlen(oneline)-1] != '\n')
      fprintf(print_file_point,"%c",'\n');
   line_number++;
   lines_this_page++;
}


/* ******************************************************** header */
/* This routine checks to see if a header needs to be printed. It  */
/* also checks for the end of a page. and spaces the paper up.     */
/* *************************************************************** */
void header(void)
{
int index;

                  /* first see if we are at the bottom of the page */
   if (lines_this_page > MAXLINES) {  /* space paper up for bottom */
   for (index = lines_this_page;index < 61;index++)
      fprintf(print_file_point,"\n");
   lines_this_page = 0;
   }

            /* put a monitor header out only at the very beginning */
   if (line_number == 0) {               /* display monitor header */
      printf("        Source file %s\n",filename);
      line_number = 1;
   }

         /* check to see if we are at the top of the page either   */
         /* through starting a file, or following a bottom of page */
   if (lines_this_page == 0) {        /* top of every printer page */
      fprintf(print_file_point,"\n\n\n        ");
      fprintf(print_file_point," Source file - %s        ",filename);
      fprintf(print_file_point,"          Page %d\n\n", page_number);
      page_number++;
   }
}


/* *************************************************** top_of_page */
/* This function spaces the paper to the top of the next page so   */
/* that another call to this function will start correctly. This   */
/* is used only at the end of a complete printout.                 */
/* *************************************************************** */
void top_of_page(void)
{
int index;

   for (index = lines_this_page;index < 61;index++)
      fprintf(print_file_point,"\n");
}

             There is no reason why the variables couldn't have been
        defined  in the LISTF.C part of the program and declared  as
        "extern"  in the LIST.C part.   Some of the variables  could
        have  been  defined  in one and some in the  other.   It  is
        merely a matter of personal taste.   Carried to an  extreme,
        all of the variables could have been defined in a third file
        and  named "extern" in both of these files.   The third file
        would then be compiled and included in the linking process.

             It would be to your advantage to compile, link, and run
        this  program to prepare you for the next program  which  is
        composed of 6 separate files which must all work together.

/*         VC.C    VC.C    VC.C    VC.C    VC.C    VC.C    VC.C

VISUAL CALCULATOR                           X   X   XXX
  MAIN PROGRAM                              X   X  X   X
                                            X   X  X
July 1, 1987                                 X X   X
                                             X X   X
                                              X    X   X
                                              X     XXX

     This program will evaluate single value expressions in a
     manner similar to those evaluated by a hand-held calculator,
     hence its name, the Visual Calculator. It was never intended
     to be programmable, so no loop constructs are included in
     its design. It is possible to write a series of statements,
     store them in a file, and recall them while using them to
     calculate with new values of input variables in the six
     variable storage registers. The input variables can be
     changed, and the entire series recalculated.

     Although this is a potentially useful program in its own
     right, it was originally written as an illustration of a
     rather large C program. It is especially useful because
     the student of C can run the program to determine its
     operating characteristics, then study the code needed to
     perform the various operations. For that reason, the entire
     program is heavily commented. An actual production program
     would probably not have as many comments as this example
     but it would not be bad practice to comment all of your
     programs to this extent.
*/
#include "ctype.h"
#include "stdio.h"
#include "string.h"
#include "conio.h"
#include "process.h"
#include "struct.def"
#include "defin.h"

struct vars allvars[12]; /* this is the main variable storage */
int varinuse = 0;        /* which variable is being used now  */
char inline[200];        /* input line area                   */
int col;                 /* used for searching across the input */
int errcode;             /* error code number                 */
int colerr;              /* column where error occurred       */
int printit = 0;         /* 1 = print a transcript            */
int ignore;              /* 1 = ignore calculations for line  */
extern char strngout[];  /* output message area               */
extern int valattr;      /* value and variable attribute      */
extern int helpattr;     /* help box attribute                */
FILE *prtfile;           /* file pointers                     */

struct lines *top, *bot, *q, *p, *arrow, *trnsend;

/* *********************************************************** main */
/* This is the main control loop for the program. It initializes    */
/* everything and reads statements until no errors are found. It    */
/* continues reading until an F10 is detected in a subordinate      */
/* function where control is returned to DOS                        */
main()
{
   top = bot = q = p = arrow = trnsend = NULL;
   monitor();                 /* initialize video attributes */
   initdata(&allvars[0]);   /* initialize all data           */
   bkgndvid();   /* display video background - double lines  */
   valusvid();   /* display starting values of all variables */
   strtrans("Welcome to the Visual Calculator - Version 1.10",0);
   transout();
   do{
      poscurs(23,7);
      printf("  input >                                 ");
      printf("                              ");
      do {                     /* repeat input until no errors      */
         readline();           /* get an input line                 */
         errdis("                         ");  /* clear error msg   */
         parse();              /* parse the line                    */
         if (errcode) errout();/* output error message              */
      } while (errcode);
      if (ignore == 1)
         strtrans(inline,0);   /* store comment in transcript       */
      else
         strtrans(inline,1);   /* store "inline" in transcript      */
      transout();
   } while (1);                /* continuous loop                   */
}
/* ******************************************************* readline */
/* This function reads a line by inputting one character at a time  */
/* and deciding what to do with it if it is a special character, or */
/* adding it to the input line if it is a special character. The    */
/* routine takes care of such things as backspace, cursor movement  */
/* and delete keys. The final result is a single line of text stored*/
/* in the buffer "inline" and the line displayed on the monitor.    */
void readline(void)
{
int index;
int c,temp;
int row = 23,col = 17;
int attr;

   if (errcode) {                   /* error recovery allow reenter */
      index = colerr;
      errcode = 0;
   }
   else {                                   /* normal input routine */
      index = 0;
      for (temp = 0;temp < 80;temp++)
         inline[temp] = 0;                    /* clear input buffer */
   }

   poscurs(row,col+index);           /* starting location of cursor */

   do {                /* repeat this do loop until a return is hit */
      while ((c = getch()) == EOF);              /* get a keystroke */

      if (c == 0) {       /* a zero here says a special key was hit */
                             /* get the key and act on it as needed */
         int spec;
         spec = getch();          /* this is the special code found */
         switch (spec) {
            case 59 : helpm();                 /* F1 - Help math    */
                      transout();
                      break;
            case 60 : helps();                 /* F2 - Help system  */
                      transout();
                      break;
            case 61 : if (printit) {           /* F3 - Print on/off */
                         printit = 0;          /* print off */
                         fprintf(prtfile,"%s\n\n","rint off");
                         fclose(prtfile);
                         strcpy(strngout,"-----");
                         attr = helpattr;
                      } else {
                         prtfile = fopen("RN","w");   /* print on */
                         if (prtprblm()) {
                            errcode = 12;   /* printer is not ready */
                            errout();
                            break;
                         }
                         printit = 1;
                         fprintf(prtfile,"%s\n","rint On");
                         strcpy(strngout,"rint");
                         attr = valattr;
                      }
                      strngdis(1,73,attr);
                      break;
            case 62 :                       /* F4 - Mark transcript */
                      arrow->marked = (arrow->marked?0:1);
                      transout();
                      break;
            case 63 : fileout();           /* F5 - Store transcript */
                      break;
            case 64 : filein();             /* F6 - Retrieve trans  */
                      errcode = 0;
                      break;
            case 65 :                       /* F7 -                 */
                      break;
            case 66 :                       /* F8 -                 */
                      break;
            case 67 :                       /* F9 - Edit a line     */
                      strcpy(inline,arrow->lineloc);
                      poscurs(23,17);
                      printf("%s",inline);
                      break;
            case 68 : poscurs(23,17);        /* F10 - Quit to DOS   */
                      printf("Quit? (Y/N) ");
                      c = getch();
                      if ((c == 'Y') || (c == 'y')){
                         clrscrn();
                         exit(0);
                      }
                      poscurs(23,17);
                      printf("              ");
                      break;
            case 75 : if (index) {                    /* left arrow */
                         index = index -1;        /* back up cursor */
                      }
                      break;

            case 77 : if (index < 65) {              /* right arrow */
                         if (inline[index] == 0)      /* zero found */
                            inline[index] = ' ';    /* blank over 0 */
                         index = index + 1;       /* cursor forward */
                      }
                      break;

            case 72 : movarrow(-1);                     /* up arrow */
                      break;

            case 80 : movarrow(1);                    /* down arrow */
                      break;

            case 73 : movarrow(-8);                      /* page up */
                      break;

            case 81 : movarrow(8);                     /* page down */
                      break;

            case 71 : movarrow(-1000);                      /* home */
                      break;

            case 79 : movarrow(1000);                        /* end */
                      break;

            case 83 : temp = index;                   /* delete key */
                              /* move all characters left one space */
                      while (inline[temp]) {
                         inline[temp] = inline[temp+1];
                         putchar(inline[temp++]);
                      }
                      putchar(0);             /* zero in last place */
                      break;

            default : poscurs(15,5);
                      printf(" S%3d",spec);
         }
         poscurs(row,col+index); /* actually put cursor in position */
      }

      else {                           /* normal letter or char hit */
         int curr,next;
         if (islower(c)) c = toupper(c);   /* convert to upper case */
         if ((c >= '\40') && (c <= '\176')) {     /* printable char */
            poscurs(row,col+index);
            putchar(c);
            next = inline[index];
            inline[index++] = c;
            curr = index;
            while((next != 0) && (curr <= 65)) {  /* move remainder */
               temp = next;                           /* line right */
               next = inline[curr];
               inline[curr++] = temp;
               putchar(temp);
            }
         }
         else {
            if ((c == 8) && index){                    /* backspace */
               index--;
               poscurs(row,col+index);            /* back up cursor */
               temp = index;
               while (inline[temp]) {
                  inline[temp] = inline[temp+1];
                  putchar(inline[temp++]);
               }
               putchar(0);
            }
         }
         poscurs(row,col+index);
      }
      if (c == 3) exit(0);                /* ctrl-break, out to DOS */
   } while (c != 13);      /* newline found, line input is complete */
}

/* ********************************************************** parse */
/* This function does a lot of checking of the input line for       */
/* logical errors in construction, then turns control over to the   */
/* function "calcdata" for the actual calculations.                 */
void parse(void)
{
int index,parcol;
double newval;
char name[7];

   varinuse = -1;
   errcode = 0;
   col = 0;
   ignore = 1;                             /* ignore this line      */

   if (inline[0] == '#') {            /* get list of variable names */
      getnames();
      return;
   }

   while (inline[col] == ' ') col++;       /* ignore leading blanks */
   if (inline[col] == '$') return;         /* ignore a comment line */
   if (inline[col] == 0) return;             /* ignore a blank line */
   ignore = 0;                            /* don't ignore this line */

   name[0] = inline[col++];                   /* find variable name */
   index = 1;
   while ((((inline[col] >= 'A') && (inline[col] <= 'Z')) ||
          ((inline[col] >= '0') && (inline[col] <= '9'))) &&
          (index <= 5)) {          /* continue var or function name */
      name[index++] = inline[col++];
   }
   name[index] = 0;                                   /* name found */

   for (index = 0;index < 12;index++) {
      if ((strcmp(name,allvars[index].varname)) == 0)
         varinuse = index;                   /* variable name found */
   }
   if (varinuse < 0) errchk(3);            /* unknown variable name */

   while (inline[col] == ' ') col++;       /* ignore leading blanks */
   if (inline[col] == '=') col++;
      else errchk(8);                         /* missing equal sign */

   parcol = 0;         /* now check for correct parenthesis matchup */
   index = col;
   do {
      if (inline[col] == '(') parcol++;
      if (inline[col++] == ')') parcol--;
      if (parcol < 0) errchk(1);       /* paren count went negative */
   } while (inline[col]);
   if (parcol) errchk(2);                  /* left over parentheses */
   col = index;

   calcdata(&newval);        /* now go evaluate the full expression */
   if (errcode == 0) {         /* don't update value if error found */
      allvars[varinuse].value = newval;
      disnew(varinuse);                /* display the changed value */
   }
}

/* ********************************************************* errout */
/* This is the function that displays the blinking error message on */
/* the monitor. Note the extra errors for expansion of the table.   */
void errout(void)
{
   switch (errcode) {
      case  1 : errdis("extra right parentheses  ");
                break;
      case  2 : errdis("missing right parentheses");
                break;
      case  3 : errdis("unknown variable name    ");
                break;
      case  4 : errdis("invalid math operator    ");
                break;
      case  5 : errdis("negative value for SQRT  ");
                break;
      case  6 : errdis("function not found      ");
                break;
      case  7 : errdis("negative value for LOG   ");
                break;
      case  8 : errdis("equal sign missing       ");
                break;
      case  9 : errdis("invalid data field       ");
                break;
      case 10 : errdis("division by zero         ");
                break;
      case 11 : errdis("File doesn't exist       ");
                break;
      case 12 : errdis("rinter not ready        ");
                break;
      case 13 : errdis("Out of memory            ");
                break;
      case 14 : errdis("Dash expected            ");
                break;
      case 15 : errdis("Invalid format code      ");
                break;
      case 16 : errdis("Neg value for FACTORIAL  ");
                break;
      case 17 : errdis("Err 17                   ");
                break;

      default : errdis("unknown error            ");
                poscurs(21,70);
                printf("%d",errcode);
   }
   poscurs(23,12+colerr);
}
        
        
                        VC.C - The Visual Calculator

             This  program  finally ties nearly everything  together
        because  it uses nearly every concept covered in the  entire
        tutorial.   It  is so big that I will not even try to  cover
        the finer points of its operation.   Only a few of the  more
        important points will be discussed.

             The  first  thing  you  should do  is  go  through  the
        tutorial  for  VC included in the file  VC.DOC.   There  are
        several  dozen  steps  for you to execute,  with  each  step
        illustrating some aspect of the Visual Calculator.  You will
        get  a  good feel for what it is capable of doing  and  make
        your study of the source code very profitable.  In addition,


                                  Page 102









                     Chapter 14 - Example Programs


        you  will  probably  find  many  ways  to  use  the   Visual
        Calculator  to  solve problems involving calculations  where
        the  simplicity  of  the problem at hand  does  not  warrant
        writing a program.

             Notice that the structure definitions,  used in all  of
        the  separate parts of the program,  are defined in the file
        STRUCT.DEF.   During  program development,  when  it  became
        necessary  to change one of the structures slightly,  it was
        not  necessary to change it in all of the  files,  only  one
        file  required modification which was then "included" in the
        source files.   Notice that the transcript data is stored in
        a doubly linked list with the data itself being stored in  a
        separate  dynamically allocated char string.   This line  is
        pointed to by the pointer "lineloc".

             For  ease  of development,  the similar functions  were
        grouped together and compiled separately.   Thus, all of the
        functions  involving the monitor were included in  the  file
        named  VIDEO.C,  and all of the functions involving the data
        storage were grouped into the FILE.C  collection.   Dividing
        your  program  in  a  way similar to  this  should  simplify
        debugging and future modifications.
        
        /*     FILE.C   FILE.C    FILE.C    FILE.C    FILE.C    FILE.C

FILE INPUT AND OUTPUT         XXXXX  XXX  X      XXXXX
                              X       X   X      X
July 1,1987                   X       X   X      X
                              XXX     X   X      XXX
                              X       X   X      X
                              X       X   X      X
                              X      XXX  XXXXX  XXXXX
*/
#include "stdio.h"
#include "string.h"
#include "alloc.h"
#include "struct.def"
#include "defin.h"

int lastline = 0;
int arrowln = 0;
extern struct lines *top, *bot, *q, *p, *arrow, *trnsend;
extern struct vars allvars[];
extern int varinuse;
extern int printit;
extern char inline[];
extern int errcode;
extern int ignore;
extern int trnsattr;

/* ******************************************************** fileout */
/* This routine opens a disk file and writes the marked lines in the*/
/* transcript to that file.                                         */
void fileout(void)
{
char *lpt, fileout[25];
struct lines *pt;
int i;
FILE *fp2;

   poscurs(23,1);                  /* read in filename to output to */
   printf("     filename >                    <");
   poscurs(23,17);
   for (i = 0;(fileout = getchar()) != '\n';++i);
   fileout = 0;                /* filename read in, ready to use */
   fp2 = fopen(fileout,"w");                           /* open file */
   pt = top;                        /* start at top of llinked list */
   do {
      lpt = pt->lineloc;                     /* line of text stored */
      if (pt->marked){                  /* only output marked lines */
         fputs(lpt,fp2);                           /* output a line */
         fputs("\n",fp2);                         /* and a linefeed */
      }
      pt = pt->dn;                             /* get the next line */
   } while (pt != NULL);
   fflush(fp2);                           /* flush the file to disk */
   fclose(fp2);                                   /* close the file */
   poscurs(23,7);
   printf("  input >                     ");
}

/* ********************************************************* filein */
/* A diskfile is opened and read into the transcript window while   */
/* all calculations are done as the file is input. If any errors are*/
/* found, the calculations are not done, zero is returned as a      */
/* result and the remainder of the file is read in. It is assumed   */
/* that the equations are correct before the file was originally    */
/* written.                                                         */
char filenam[25] = "help";        /* default filename to start with */
void filein(void)
{
char filein[25];
char *fc;
int i;
FILE *fp2;

   poscurs(23,1);                      /*read in filename for input */
   printf("     filename >                    <");
   poscurs(23,17);
   for (i = 0; (filein = getchar()) != '\n';++i);
   filein = 0;                /* filename read in , ready to use */
   if (filein[0] == 0)                  /* if no filename was input */
      strcpy(filein,filenam);            /* use last valid filemane */
   else
      strcpy(filenam,filein);                 /* save for later use */
   fp2 = fopen(filein,"r");                            /* open file */
   if (fp2 == NULL) {                         /* file doesn't exist */
      errcode = 11;
      errout();
   }
   else {
      do {
         fc = (fgets(inline,62,fp2));
         if (fc == NULL) break;
         for (i=0;inline;++i);
         inline[i-1] = 0;
          parse();
         if ((ignore == 1) || errcode)
            strtrans(inline,0);
         else
            strtrans(inline,1);
         transout();
      } while (i != NULL);
   }
   for (i = 0;i < 200;++i) inline = 0;       /* clear input area */
   poscurs(23,7);
   printf("  input >                      ");
}

/* ******************************************************* strtrans */
/* A line from the input area or from a file input is stored in the */
/* transcript area. It is stored in the transcript array here, and  */
/* output to the transcript window in the "transout" function.      */
/* This function uses a linked list to store the lines of data.     */
void strtrans(char line[],int type)
{
int i;
long int temp;
char *pt;
char buffer[25];     /* this is long enough to include an overwrite */
double xx;                                    /* temporary variable */
extern FILE *prtfile;                          /* print file output */

   p = (struct lines *)malloc(sizeof(struct lines));
   pt = (char *)malloc(1 + strlen(line));
   if ((p == NULL) || (pt == NULL)) {              /* out of memory */
      errcode = 13;
      errout();
   }

   else {                  /* there is enough memory for this entry */
      if (top == NULL){                               /* first entry */
          top = bot = p;
          p->dn = NULL;
          p->up = NULL;
      }
      else {                                  /* additional entries */
          bot->dn = p;
          p->up = bot;
          p->dn = NULL;
          bot = p;
      }

      p->lineloc = pt;
      i = strlen(line);
      p->isvalue = type;
      p->marked = type;
      p->linelngt = i;
      if (type) {
         xx = allvars[varinuse].value;
         if (xx < 0.0) xx = -xx;
         if ((xx > 9999999.0) || (xx < .001))
            sprintf(buffer,"%12.5e",allvars[varinuse].value);
         else
            sprintf(buffer,"%12.6f",allvars[varinuse].value);
         buffer[12] = 0;
         if (varinuse > 5) {                /* variable I through N */
            temp = allvars[varinuse].value;
            temp = temp & 077777777;
            if (allvars[varinuse].outtype == 'D')
               sprintf(buffer,"(D) %8ld",temp);
            if (allvars[varinuse].outtype == 'O')
               sprintf(buffer,"(O) %8lo",temp);
            if ((allvars[varinuse].outtype == 'X') ||
                (allvars[varinuse].outtype == 'H'))
               sprintf(buffer,"(H) %8lx",temp);
         }
         strcpy(p->strval,buffer);
      }
      else
         strcpy(p->strval,"            ");
      line = '\0';                            /* line terminator */
      strcpy(pt,line);
      if (type && printit){
         fprintf(prtfile,"%13s  %-62s\n",buffer,line);
      }
      arrow = p;
      trnsend = p;
      lastline++;
      arrowln = lastline;
   }
}

/* ******************************************************* transout */
/* This function outputs the transcript to the transcript window    */
extern char strngout[];
void transout(void)
{
int i;
int maxm = 13;       /* number of lines to output to the trans wind */
char *pt;
   p = trnsend;
   for (i = 0;i < maxm;++i){           /* count up max from trnsend */
      if (p->up == NULL) break;                /* stop if top found */
      p = p->up;
   }
   for (i = 0;i <= maxm;++i){       /* output max fields to viddisp */
      pt = p->lineloc;                 /* pt now points to the line */
      strcpy(strngout,p->strval);
      strngdis(8+i,1,trnsattr);       /* output the formatted value */
      strcpy(strngout,pt);
      blnkline(8+i,16);                     /* write blanks to line */
      if (p->marked)
         chardis(8+i,15,trnsattr,'*');          /* marked indicator */
      else
         chardis(8+i,15,trnsattr,' ');                     /* blank */
      strngdis(8+i,17,trnsattr);
      if (arrow == p)
         chardis(8+i,14,trnsattr,16);                 /* arrow char */
      else
         chardis(8+i,14,trnsattr,' ');                     /* blank */
      if (p->dn == NULL) break;             /* stop if bottom found */
      p = p->dn;
   }
   poscurs(23,3);
   printf("%4d",arrowln);
}

/* ******************************************************* movarrow */
/* This function is used to move the arrow up or down in the window */
/* and to control where the window begins and ends in the transcript*/
/* data. The arrow is always two lines from the top or bottom if it */
/* is possible to do so.                                            */
void movarrow(int where)
{
int index;
struct lines *temp;
int iend, iarrow, itrnsend;
   iend = iarrow = itrnsend = 0;
   if (where > 0) {
      for (index = where;index && (arrow != bot);--index)
         arrow = arrow->dn;                  /* move arrow down one */
      for (temp = top,index = 0;temp != bot;index++) {
         if (temp == arrow) iarrow = index;         /* locate arrow */
         if (temp == trnsend) itrnsend = index;  /* loc display end */
         temp = temp->dn;
      }
      if (temp == arrow) iarrow = index;          /* if they are at */
      if (temp == trnsend) itrnsend = index;      /* the bottom end */
      iend = index;
           /* now trnsend must be >= arrow, but not by more than 10 */
      if (iarrow == iend) index = iend - itrnsend;
      else if (itrnsend < (iarrow+1)) index = iarrow - itrnsend + 1;
      else index = 0;
   }
   else {
      for (index = -where;index && (arrow != top);--index)
         arrow = arrow->up;                    /* move arrow up one */
/*    if (arrow == top) arrow = arrow->dn;      move one field down */
      for (temp = top,index = 0;temp != bot;index++) {
         if (temp == arrow) iarrow = index;         /* locate arrow */
         if (temp == trnsend) itrnsend = index;  /* loc display end */
         temp = temp->dn;
      }
      if (temp == arrow) iarrow = index;          /* if they are at */
      if (temp == trnsend) itrnsend = index;      /* the bottom end */
      iend = index;
           /* now trnsend must be >= arrow, but not by more than 12 */
      if (iarrow == 0) index = (iend > 13?13:iend) - itrnsend;
      else if ((itrnsend - iarrow) > 12) index = iarrow-itrnsend+12;
      else index = 0;
   }
   if (index > 0)
      for (;index > 0;--index)
         trnsend = trnsend->dn;
   else if (index < 0)
      for (;index < 0;++index)
         trnsend = trnsend->up;
   arrowln = iarrow;
   transout();
}


             Of special interest is the "monitor()" function.   This
        function  examines  the  video mode through  use  of  a  DOS
        command  and  if it is a 7,  it assumes it is  a  monochrome
        monitor,  otherwise it assumes a color monitor.   The colors
        of  the various fields are established at this time and used
        throughout  the  program.   Most  of  the  data  is  written
        directly  to the video memory,  but some is written  through
        the standard BIOS routines.

/*
FILENAME      FUNCTION    DESCRIPTION OF FUNCTION


DEFIN.C     ----------->  Text file - definition only - this file

VC.C        main()      primarily a menu
            readline()  read an input line from the keyboard
            parse()     parse the input line
            errout()    output error message to display

DATA.C      initdata()  initialize data and fields
            getnames()  get new variable names
            calcdata()  do the calculations
            calcdat()   do the four function calculations
            getnum()    get the value of the number
            getop()     get the math operator
            errchk()    check for error storage

FILE.C      fileout()   store transcript to a file
            filein()    retrieve transcript from a file
            strtrans()  store a transcript message
            transout()  display the current transcript
            movarrow()  move the arrow up or down

VIDEO.C     monitor()   determine type of monitor
            bkgndvid()  display video background
            valusvid()  display all values
            disnew()    display the new changed variable
            helpm()     display mathematics help messages
            helps()     display system help messages
            linedisp()  display a line to video (with attr)
            strngdis()  display a line (add attr)
            blnkline()  outputs blanks to a video line
            chardis()   display a single character
            errdis()    display error message to screen
            clrscrn()   clear the video monitor
            poscurs()   position the cursor on the monitor
            prtprblm()  printer problem check
                                                                */

/*  Prototype definitions for all functions                     */

void readline(void);
void parse(void);
void errout(void);

void initdata(struct vars *pnt);
void getnames(void);
void calcdata(double *newval);
void calcdat(double *number1,char *op1,double *number2);
double getnum(void);
char getop(void);
void errchk(int err);

void fileout(void);
void filein(void);
void strtrans(char line[],int type);
void transout(void);
void movarrow(int where);

void monitor(void);
void bkgndvid(void);
void valusvid(void);
void disnew(int varinuse);
void helpm(void);
void helps(void);
void linedisp(int line);
void strngdis(int row,int col,int attr);
void blnkline(int row,int col);
void chardis(int row,int col,int attr,int ch);
void errdis(char str[]);
void clrscrn(void);
void poscurs(int row,int col);
int prtprblm(void);
        
        
             The file DEFIN.H is a catalogue of the functions to aid
        in finding the functions.  This file was generated as one of
        the  first  files  and was maintained and  updated  for  use
        during  the  entire  design and coding  lifetime.   It  also
        contains all of the prototype definitions for the  functions
        in  all  of  the source files, and is  "included"  in  every
        source file to do prototype checking.













                                  Page 103

---- END ---

TOP

C tutorial Chapter 13 - Character and Bit Manipulation
Chapter 13 - Character and Bit Manipulation


                            UPPER AND LOWER CASE

                           
                                        /* Chapter 13 - Program 1 */
#include "STDIO.H"
#include "ctype.h"      /* Note - your compiler may not need this */

main()
{
FILE *fp;
char line[80], filename[24];
char *c;

   printf("Enter filename -> ");
   scanf("%s",filename);
   fp = fopen(filename,"r");

   do {
      c = fgets(line,80,fp);   /* get a line of text */
      if (c != NULL) {
         mix_up_the_chars(line);
      }
   } while (c != NULL);

   fclose(fp);
}

mix_up_the_chars(line)   /* this function turns all upper case
                            characters into lower case, and all
                            lower case to upper case. It ignores
                            all other characters.               */
char line[];
{
int index;

   for (index = 0;line[index] != 0;index++) {
      if (isupper(line[index]))     /* 1 if upper case */
         line[index] = tolower(line[index]);
      else {
         if (islower(line[index]))  /* 1 if lower case */
            line[index] = toupper(line[index]);
      }
   }
   printf("%s",line);
}
                           
                           
             Load  and display the program UPLOW.C for an example of
        a  program that does lots of character  manipulation.   More
        specifically,  it changes the case of alphabetic  characters
        around.   It illustrates the use of four functions that have
        to  do with case.   It should be no problem for you to study
        this program on your own and understand how it  works.   The
        four functions on display in this program are all within the
        user written function, "mix_up_the_chars".  Compile and  run
        the  program  with  the  file  of  your  choice.   The  four
        functions are;

             isupper();     Is the character upper case?
             islower();     Is the character lower case?
             toupper();     Make the character upper case.
             tolower();     Make the character lower case.

             Many more Classification and Conversion routines should
        be listed in the reference material for your compiler.

                        CLASSIFICATION OF CHARACTERS

                                         /* Chapter 13 - Program 2 */
#include "stdio.h"
#include "ctype.h"       /* Note - your compiler may not need this */

main()
{
FILE *fp;
char line[80], filename[24];
char *c;

   printf("Enter filename -> ");
   scanf("%s",filename);
   fp = fopen(filename,"r");

   do {
      c = fgets(line,80,fp);   /* get a line of text */
      if (c != NULL) {
         count_the_data(line);
      }
   } while (c != NULL);

   fclose(fp);
}

count_the_data(line)
char line[];
{
int whites, chars, digits;
int index;

   whites = chars = digits = 0;

   for (index = 0;line[index] != 0;index++) {
      if (isalpha(line[index]))   /* 1 if line[] is alphabetic  */
          chars++;
      if (isdigit(line[index]))   /* 1 if line[] is a digit     */
          digits++;
      if (isspace(line[index]))   /* 1 if line[] is blank, tab, */
          whites++;               /*           or newline       */
   }   /* end of counting loop */

   printf("%3d%3d%3d %s",whites,chars,digits,line);
}
                        
                        
             Load  and display the next program, CHARCLAS.C  for  an
        example of character counting.  We have repeatedly used  the
        backslash  n character representing a new line.   These  are
        called escape sequences, and some of the more commonly  used
        are defined in the following table;

             \n             Newline
             \t             Tab
             \b             Backspace
             \"             Double quote
             \\             Backslash
             \0             NULL (zero)

             Consult your compiler documentation for a complete list
        of escape sequences available with your compiler.

             By  preceding  each of the above  characters  with  the
        backslash character, the character can be included in a line
        of text for display,  or printing.   In the same way that it
        is  perfectly  all right to use the letter "n" in a line  of
        text as a part of someone's name, and as an end-of-line, the
        other  characters can be used as parts of text or for  their
        particular functions.

             The program on your screen uses the functions that  can
        determine   the  class  of  a  character,   and  counts  the
        characters  in  each class.   The number of  each  class  is



                                   Page 96









                 Chapter 13 - Character and Bit Manipulation


        displayed  along with the line itself.   The three functions
        are as follows;

             isalpha();     Is the character alphabetic?
             isdigit();     Is the character a numeral?
             isspace();     Is the character any of, \n, \t,
                              or blank?

             As  noted above, many more Classification Routines  are
        available with your compiler.

             This program should be simple for you to find your  way
        through  so no explanation will be given.   It was necessary
        to give an example with these functions used.   Compile  and
        run this program with any file you choose.

                           THE LOGICAL FUNCTIONS

                           
                                        /* Chapter 13 - Program 3 */
main()
{
char mask;
char number[6];
char and,or,xor,inv,index;

   number[0] = 0X00;
   number[1] = 0X11;
   number[2] = 0X22;
   number[3] = 0X44;
   number[4] = 0X88;
   number[5] = 0XFF;

   printf(" nmbr  mask   and    or   xor   inv\n");
   mask = 0X0F;
   for (index = 0;index <= 5;index++) {
      and = mask & number[index];
      or = mask | number[index];
      xor = mask ^ number[index];
      inv = ~number[index];
      printf("%5x %5x %5x %5x %5x %5x\n",number[index],
              mask,and,or,xor,inv);
   }

   printf("\n");
   mask = 0X22;
   for (index = 0;index <= 5;index++) {
      and = mask & number[index];
      or = mask | number[index];
      xor = mask ^ number[index];
      inv = ~number[index];
      printf("%5x %5x %5x %5x %5x %5x\n",number[index],
              mask,and,or,xor,inv);
   }
}
                           
             Load and display the program BITOPS.C. The functions in
        this  group of functions are used to do bitwise  operations,
        meaning  that  the operations are performed on the  bits  as
        though they were individual bits.   No carry from bit to bit
        is performed as would be done with a binary addition.   Even
        though  the operations are performed on a single bit  basis,
        an entire byte or integer variable can be operated on in one
        instruction.   The operators and the operations they perform
        are given in the following table;

             &    Logical AND, if both bits are 1, the result is 1.
             |    Logical OR, if either bit is one, the result is 1.
             ^    Logical XOR,  (exclusive OR),  if one and only one
                    bit is 1, the result is 1.
             ~    Logical invert,  if the bit is 1, the result is 0,
                    and if the bit is 0, the result is 1.

             The  example  program  uses  several  fields  that  are
        combined  in each of the ways given above.   The data is  in
        hexadecimal  format.   It  will be assumed that you  already
        know hexadecimal format if you need to use these operations.
        If  you  don't,  you  will need to study  it  on  your  own.
        Teaching  the  hexadecimal format of numbers is  beyond  the
        scope of this tutorial.

             Run the program and observe the output.

                           THE SHIFT INSTRUCTIONS

                                         /* Chapter 13 - Program 4 */
main()
{
int small, big, index, count;

   printf("       shift left      shift right\n\n");
   small = 1;
   big = 0x4000;
   for(index = 0;index < 17;index++) {
      printf("%8d %8x %8d %8x\n",small,small,big,big);
      small = small << 1;
      big = big >> 1;
   }

   printf("\n");
   count = 2;
   small = 1;
   big = 0x4000;
   for(index = 0;index < 9;index++) {
      printf("%8d %8x %8d %8x\n",small,small,big,big);
      small = small << count;
      big = big >> count;
   }
}
                           
                           
             The  last two operations to be covered in this  chapter
        are  the left shift and the right shift instructions.   Load
        the example program SHIFTER.C for an example using these two



                                   Page 97









                 Chapter 13 - Character and Bit Manipulation


        instructions.    The   two  operations  use  the   following
        operators;

             << n      Left shift n places.
             >> n      Right shift n places.

             Once again the operations are carried out and displayed
        using the hexadecimal format.   The program should be simple
        for you to understand on your own, there is no tricky code.












































                                   Page 98

TOP

C tutorial Chapter 12 - Dynamic Allocation
Chapter 12 - Dynamic Allocation


                        WHAT IS DYNAMIC ALLOCATION?

                                         /* Chapter 12 - Program 1 */
main()
{
struct animal {
   char name[25];
   char breed[25];
   int age;
} *pet1, *pet2, *pet3;

   pet1 = (struct animal *)malloc(sizeof(struct animal));
   strcpy(pet1->name,"General");
   strcpy(pet1->breed,"Mixed Breed");
   pet1->age = 1;

   pet2 = pet1;   /* pet2 now points to the above data structure */

   pet1 = (struct animal *)malloc(sizeof(struct animal));
   strcpy(pet1->name,"Frank");
   strcpy(pet1->breed,"Labrador Retriever");
   pet1->age = 3;

   pet3 = (struct animal *)malloc(sizeof(struct animal));
   strcpy(pet3->name,"Krystal");
   strcpy(pet3->breed,"German Shepherd");
   pet3->age = 4;

       /* now print out the data described above */

   printf("%s is a %s, and is %d years old.\n", pet1->name,
           pet1->breed, pet1->age);

   printf("%s is a %s, and is %d years old.\n", pet2->name,
           pet2->breed, pet2->age);

   printf("%s is a %s, and is %d years old.\n", pet3->name,
           pet3->breed, pet3->age);

   pet1 = pet3;   /* pet1 now points to the same structure that
                      pet3 points to                           */
   free(pet3);    /* this frees up one structure               */
   free(pet2);    /* this frees up one more structure          */
/* free(pet1);    this cannot be done, see explanation in text */
}
                        
                        
             Dynamic allocation is very intimidating to a person the
        first time he comes across it, but that need not be.  Simply
        relax  and  read this chapter carefully and you will have  a
        good grounding in a very valuable programming resource.  All
        of the variables in every program up to this point have been
        static  variables as far as we  are  concerned.   (Actually,
        some  of  them  have been "automatic" and  were  dynamically
        allocated for you by the system,  but it was transparent  to
        you.)   In  this  chapter,  we will study  some  dynamically
        allocated  variables.  They are variables that do not  exist
        when  the program is loaded, but are created dynamically  as
        they are needed.  It is possible, using these techniques, to
        create as many variables as needed, use them, and deallocate
        their space for use by other variables.  As usual, the  best
        teacher is an example, so load and display the program named
        DYNLIST.C.

             We  begin by defining a named structure "animal" with a
        few  fields  pertaining  to dogs.   We  do  not  define  any
        variables of this type,  only three pointers.  If you search
        through  the  remainder  of the program,  you will  find  no
        variables defined so we have nothing to store data in.   All
        we have to work with are three pointers, each of which point
        to the defined structure.   In order to do anything, we need
        some variables, so we will create some dynamically.

                         DYNAMIC VARIABLE CREATION

             The first program statement, which assigns something to
        the   pointer  "pet1"  will  create  a   dynamic   structure
        containing  three variables.   The heart of the statement is
        the "malloc" function buried in the middle of the statement.
        This  is a "memory allocate" function that needs  the  other
        things to completely define it.   The "malloc" function,  by
        default, will allocate a piece of memory on a "heap" that is
        "n" characters in length and will be of type character.  The
        "n"  must be specified as the only argument to the function.
        We will discuss "n" shortly,  but first we need to define  a
        "heap".

                              WHAT IS A HEAP?

             Every compiler has a set of limitations on it as to how
        big  the executable file can be,  how many variables can  be
        used,  how long the source file can be, etc.  One limitation
        placed  on users by most C compilers is a limit of  64K  for
        the executable code if you happen to be in the small  memory
        model.   This  is because the IBM-PC uses  a  microprocessor
        with  a 64K segment size, and it requires special  calls  to


                                  Page 87









                      Chapter 12 - Dynamic Allocation


        use data outside of a single segment.  In order to keep  the
        program  small and efficient, these calls are not used,  and
        the  memory  space is limited but still  adequate  for  most
        programs.

             A  heap is an area outside of this 64K  boundary  which
        can be accessed by the program to store data and  variables.
        The  data and variables are put on the "heap" by the  system
        as  calls to "malloc" are made.  The system keeps  track  of
        where  the  data  is  stored.  Data  and  variables  can  be
        deallocated  as desired leading to holes in the  heap.   The
        system  knows  where  the holes are and will  use  them  for
        additional  data  storage as more "malloc" calls  are  made.
        The  structure  of  the heap is  therefore  a  very  dynamic
        entity, changing constantly.

                            MORE ABOUT SEGMENTS

             Most  C  compilers  give the user a  choice  of  memory
        models to use. The user has a choice of using a model with a
        64K limitation for either program or data leading to a small
        fast  program or selecting a 640K limitation  and  requiring
        longer  address calls leading to less efficient  addressing.
        Using  the  larger  address  space  requires  inter  segment
        addressing,  resulting in the slightly slower running  time.
        The  time  is probably insignificant in most  programs,  but
        there are other considerations.

             If a program uses no more than 64K bytes for the  total
        of its code and memory and if it doesn't use a stack, it can
        be made into a .COM file.  Since a .COM file is already in a
        memory image format, it can be loaded very quickly whereas a
        file in an .EXE format must have its addresses relocated  as
        it is loaded.  Therefore a tiny memory model can generate  a
        program  that loads faster than one generated with a  larger
        memory model.  Don't let this worry you, it is a fine  point
        that few programmers worry about.

             Using  dynamic allocation, it is possible to store  the
        data  on the "heap" and that may be enough to allow  you  to
        use  the small memory model.  Of course, you wouldn't  store
        local  variables such as counters and indexes on  the  heap,
        only very large arrays or structures.

             Even  more  important than the need to stay within  the
        small memory model is the need to stay within the  computer.
        If  you  had a program that used several large data  storage
        areas,  but not at the same time,  you could load one  block
        storing  it  dynamically,  then get rid of it and reuse  the
        space for the next large block of data.  Dynamically storing
        each block of data in succession, and using the same storage


                                  Page 88









                      Chapter 12 - Dynamic Allocation


        for  each block may allow you to run your entire program  in
        the computer without breaking it up into smaller programs.

                       BACK TO THE "MALLOC" FUNCTION

             Hopefully  the above description of the "heap" and  the
        overall plan for dynamic allocation helped you to understand
        what  we are doing with the "malloc"  function.   It  simply
        asks the system for a block of memory of the size specified,
        and  gets  the block with the pointer pointing to the  first
        element of the block.   The only argument in the parentheses
        is the size of the block desired and in our present case, we
        desire  a  block  that will hold one of  the  structures  we
        defined at the beginning of the program.  The "sizeof" is  a
        new  function, new to us at least, that returns the size  in
        bytes of the argument within its parentheses.  It therefore,
        returns the size of the structure named "animal", in  bytes,
        and  that  number is sent to the system  with  the  "malloc"
        call.   At the completion of that call, we have a  block  on
        the heap allocated to us, with "pet1" pointing to the  block
        of data.

                              WHAT IS A CAST?

             We  still  have  a  funny  looking  construct  at   the
        beginning  of the "malloc" function call.   That is called a
        "cast".   The  "malloc"  function returns a block  with  the
        pointer  pointing  to it being a pointer of type  "char"  by
        default.  Many times, if not most, you do not want a pointer
        to a "char" type variable,  but to some other type.  You can
        define  the  pointer type with the construct  given  on  the
        example line.   In this case we want the pointer to point to
        a  structure of type "animal",  so we tell the compiler with
        this strange looking construct.   Even if you omit the cast,
        most compilers will return a pointer correctly,  give you  a
        warning,  and  go  on to produce a working program.   It  is
        better programming practice to provide the compiler with the
        cast to prevent getting the warning message.

                USING THE DYNAMICALLY ALLOCATED MEMORY BLOCK

             If you remember our studies of structures and pointers,
        you  will recall that if we have a structure with a  pointer
        pointing  to it,  we can access any of the variables  within
        the structure.   In the next three lines of the program,  we
        assign  some silly data to the structure  for  illustration.
        It  should come as no surprise to you that these  assignment
        statements  look just like assignments to statically defined
        variables.




                                  Page 89









                      Chapter 12 - Dynamic Allocation


             In the next statement, we assign the value of "pet1" to
        "pet2" also.   This creates no new data,  we simply have two
        pointers  to the same object.   Since "pet2" is pointing  to
        the structure we created above,  "pet1" can be reused to get
        another  dynamically allocated structure which is just  what
        we  do next.   Keep in mind that "pet2" could have  just  as
        easily been used for the new allocation.   The new structure
        is filled with silly data for illustration.

             Finally,  we  allocate another block on the heap  using
        the  pointer  "pet3",  and fill its block with  illustrative
        data.

             Printing  the  data out should pose no problem  to  you
        since  there is nothing new in the three  print  statements.
        It is left for you to study.

             Even though it is not illustrated in this tutorial, you
        can dynamically allocate and use simple variables such as  a
        single  "char"  type variable.  This should  be  discouraged
        however since it is very inefficient.

               GETTING RID OF THE DYNAMICALLY ALLOCATED DATA

             Another new function is used to get rid of the data and
        free  up  the  space on the heap  for  reuse,  the  function
        "free".   To use it,  you simply call it with the pointer to
        the   block  as  the  only  argument,   and  the  block   is
        deallocated.

             In  order  to illustrate another aspect of the  dynamic
        allocation and deallocation of data,  an additional step  is
        included in the program on your monitor.  The pointer "pet1"
        is assigned the value of "pet3" in line 38.  In doing  this,
        the  block that "pet1" was pointing to is  effectively  lost
        since  there  is  no pointer that is now  pointing  to  that
        block.   It  can  therefore  never  again  be  referred  to,
        changed,  or disposed of.  That memory, which is a block  on
        the  heap,  is  wasted  from this point  on.   This  is  not
        something that you would ever purposely do in a program.  It
        is only done here for illustration.

             The  first  "free" function call removes the  block  of
        data that "pet1" and "pet3" were pointing to, and the second
        "free"  call  removes  the block of  data  that  "pet2"  was
        pointing  to.   We therefore have lost access to all of  our
        data  generated earlier.   There is still one block of  data
        that  is on the heap but there is no pointer to it since  we
        lost  the address to it.   Trying to "free" the data pointed
        to by "pet1" would result in an error because it has already
        been  "freed"  by the use of "pet3".  There is  no  need  to


                                  Page 90









                      Chapter 12 - Dynamic Allocation


        worry,  when  we  return to DOS, the  entire  heap  will  be
        disposed  of with no regard to what we have put on it.   The
        point  does  need to made that, if you lose a pointer  to  a
        block  of  the heap, it forever removes that block  of  data
        storage from our use and we may need that storage later.

             Compile and run the program to see if it does what  you
        think it should do based on this discussion.

                        THAT WAS A LOT OF DISCUSSION

             It took nearly four pages to get through the discussion
        of  the last program but it was time well spent.   It should
        be  somewhat exciting to you to know that there  is  nothing
        else to learn about dynamic allocation,  the last four pages
        covered  it all.   Of course,  there is a lot to learn about
        the  technique  of using dynamic allocation,  and  for  that
        reason,  there  are two more files to study.   But the  fact
        remains,  there  is  nothing  more to  learn  about  dynamic
        allocation than what was given so far in this chapter.

                            AN ARRAY OF POINTERS

                                      /* Chapter 12 - Program 2 */
main()
{
struct animal {
   char name[25];
   char breed[25];
   int age;
} *pet[12], *point;   /* this defines 13 pointers, no variables */
int index;

            /* first, fill the dynamic structures with nonsense */
   for (index = 0;index < 12;index++) {
      pet[index] = (struct animal *)malloc(sizeof(struct animal));
      strcpy(pet[index]->name,"General");
      strcpy(pet[index]->breed,"Mixed Breed");
      pet[index]->age = 4;
   }

   pet[4]->age = 12;        /* these lines are simply to        */
   pet[5]->age = 15;        /*      put some nonsense data into */
   pet[6]->age = 10;        /*            a few of the fields.  */

       /* now print out the data described above */

   for (index = 0;index <12;index++) {
      point = pet[index];
      printf("%s is a %s, and is %d years old.\n", point->name,
              point->breed, point->age);
   }

       /* good programming practice dictates that we free up the */
       /* dynamically allocated space before we quit.            */

   for (index = 0;index < 12;index++)
      free(pet[index]);
}
                           
             Load and display the file BIGDYNL.C for another example
        of dynamic allocation.   This program is very similar to the
        last one since we use the same structure,  but this time  we
        define an array of pointers to illustrate the means by which
        you  could build a large database using an array of pointers
        rather  than a single pointer to each element.   To keep  it
        simple  we  define  12 elements in  the  array  and  another
        working pointer named "point".

             The "*pet[12]" is new to you so a few words would be in
        order.  What we have defined is an array of 12 pointers, the
        first  being "pet[0]",  and the last  "pet[11]".   Actually,
        since an array is itself a pointer, the name "pet" by itself
        is a pointer to a pointer.   This is valid in C, and in fact
        you  can  go  farther  if needed but you  will  get  quickly
        confused.   I  know  of no limit as to how  many  levels  of
        pointing are possible,  so a definition such as "int ****pt"
        is legal as a pointer to a pointer to a pointer to a pointer
        to an integer type variable, if I counted right.  Such usage
        is discouraged until you gain considerable experience.

             Now that we have 12 pointers which can be used like any
        other  pointer,  it  is a simple matter to write a  loop  to
        allocate  a data block dynamically for each and to fill  the
        respective  fields with any data desirable.   In this  case,
        the  fields  are  filled with simple data  for  illustrative
        purposes,  but we could be reading in a  database,  readings
        from some test equipment, or any other source of data.


                                  Page 91









                      Chapter 12 - Dynamic Allocation



             A  few fields are randomly picked to receive other data
        to illustrate that simple assignments can be used,  and  the
        data is printed out to the monitor.   The pointer "point" is
        used  in the printout loop only to serve as an illustration,
        the  data could have been easily printed using the  "pet[n]"
        means  of definition.   Finally,  all 12 blocks of data  are
        freed before terminating the program.

             Compile  and run this program to aid  in  understanding
        this  technique.   As stated earlier,  there was nothing new
        here  about  dynamic  allocation,  only about  an  array  of
        pointers.

                               A LINKED LIST

                                          /* Chapter 12 - Program 3 */
#include "stdio.h"    /* this is needed only to define the NULL     */
#define RECORDS 6

main()
{
struct animal {
   char name[25];       /* The animals name                         */
   char breed[25];      /* The type of animal                       */
   int age;             /* The animals age                          */
   struct animal *next; /* a pointer to another record of this type */
} *point, *start, *prior; /* this defines 3 pointers, no variables  */
int index;

                       /* the first record is always a special case */

   start = (struct animal *)malloc(sizeof(struct animal));
   strcpy(start->name,"General");
   strcpy(start->breed,"Mixed Breed");
   start->age = 4;
   start->next = NULL;
   prior = start;
       /* a loop can be used to fill in the rest once it is started */

   for (index = 0;index < RECORDS;index++) {
      point = (struct animal *)malloc(sizeof(struct animal));
      strcpy(point->name,"Frank");
      strcpy(point->breed,"Laborador Retriever");
      point->age = 3;
      prior->next = point;  /* point last "next" to this record */
      point->next = NULL;   /* point this "next" to NULL        */
      prior = point;        /* this is now the prior record     */
   }

       /* now print out the data described above */

   point = start;
   do {
      prior = point->next;
      printf("%s is a %s, and is %d years old.\n", point->name,
              point->breed, point->age);
      point = point->next;
   } while (prior != NULL);

       /* good programming practice dictates that we free up the */
       /* dynamically allocated space before we quit.            */

   point = start;            /* first block of group      */
   do {
      prior = point->next;   /* next block of data        */
      free(point);           /* free present block        */
      point = prior;         /* point to next             */
   } while (prior != NULL);  /* quit when next is NULL    */
}
                              
                              
             We  finally  come to the grandaddy of  all  programming
        techniques as far as being intimidating.   Load the  program
        DYNLINK.C  for an example of a dynamically allocated  linked
        list.   It  sounds terrible,  but after a little time  spent
        with it,  you will see that it is simply another programming
        technique  made  up  of  simple components  that  can  be  a
        powerful tool.

             In order to set your mind at ease,  consider the linked
        list  you used when you were a child.   Your sister gave you
        your birthday present,  and when you opened it,  you found a
        note that said,  "Look in the hall closet."  You went to the
        hall closet,  and found another note that said, "Look behind
        the  TV  set."  Behind the TV you found  another  note  that
        said,  "Look  under  the  coffee pot."  You  continued  this
        search,  and finally you found your pair of socks under  the
        dogs  feeding dish.   What you actually did was to execute a
        linked  list,  the starting point being the wrapped  present
        and the ending point being under the dogs feeding dish.  The
        list ended at the dogs feeding dish since there were no more
        notes.

             In  the program DYNLINK.C,  we will be doing  the  same
        thing as your sister forced you to do.   We will however, do
        it  much  faster and we will leave a little pile of data  at
        each of the intermediate points along the way.  We will also
        have   the  capability  to  return  to  the  beginning   and
        retraverse the entire list again and again if we so desire.

                            THE DATA DEFINITIONS

             This program starts similarly to the last two with  the
        addition  of the definition of a constant to be used  later.
        The  structure  is nearly the same as that used in the  last
        two programs except for the addition of another field within


                                  Page 92









                      Chapter 12 - Dynamic Allocation


        the  structure in line 11, the pointer.  This pointer  is  a
        pointer  to another structure of this same type and will  be
        used  to point to the next structure in order.  To  continue
        the above analogy, this pointer will point to the next note,
        which in turn will contain a pointer to the next note  after
        that.

             We  define three pointers to this structure for use  in
        the program, and one integer to be used as a counter, and we
        are ready to begin using the defined structure for  whatever
        purpose  we  desire.   In  this case,  we  will  once  again
        generate nonsense data for illustrative purposes.

                              THE FIRST FIELD

             Using  the  "malloc" function,  we request a  block  of
        storage on the "heap" and fill it with data.  The additional
        field in this example, the pointer, is assigned the value of
        NULL, which is only used to indicate that this is the end of
        the  list.   We  will  leave  the pointer  "start"  at  this
        structure,  so  that  it  will always  point  to  the  first
        structure of the list.   We also assign "prior" the value of
        "start" for reasons we will see soon.  Keep in mind that the
        end  points of a linked list will always have to be  handled
        differently  than those in the middle of a list.   We have a
        single  element  of  our  list now and  it  is  filled  with
        representative data.

                       FILLING ADDITIONAL STRUCTURES

             The  next group of assignments and  control  statements
        are  included  within a "for" loop so we can build our  list
        fast  once  it is defined.   We will go through the  loop  a
        number  of times equal to the constant "RECORDS" defined  at
        the  beginning  of  our  program.   Each  time  through,  we
        allocate memory,  fill the first three fields with nonsense,
        and  fill the pointers.   The pointer in the last record  is
        given  the  address of this new record because  the  "prior"
        pointer is pointing to the prior record.  Thus "prior->next"
        is given the address of the new record we have just  filled.
        The  pointer in the new record is assigned the value "NULL",
        and  the  pointer "prior" is given the address of  this  new
        record  because the next time we create a record,  this  one
        will  be  the  prior  one at  that  time.   That  may  sound
        confusing  but it really does make sense if you  spend  some
        time studying it.

             When  we have gone through the "for" loop 6  times,  we
        will  have  a  list  of 7 structures including  the  one  we
        generated  prior  to  the loop.   The  list  will  have  the
        following characteristics.


                                  Page 93









                      Chapter 12 - Dynamic Allocation




        1. "start" points to the first structure in the list.

        2. Each structure contains a pointer to the next structure.

        3. The last structure has a pointer  that points to NULL and
           can be used to detect the end.

           start->struct1              This diagram should aid in
                  name              understanding the structure of
                  breed             the data at this point.
                  age
                  point->struct2
                         name
                         breed
                         age
                         point->struct3
                                name
                                breed
                                age
                                point-> . . . . struct7
                                                name
                                                breed
                                                age
                                                point->NULL


             It should be clear to you,  if you understand the above
        structure,  that  it is not possible to simply jump into the
        middle of the structure and change a few values.   The  only
        way  to  get  to the third structure is by starting  at  the
        beginning  and working your way down through  the  structure
        one  record at a time.   Although this may seem like a large
        price  to  pay for the convenience of putting so  much  data
        outside of the program area,  it is actually a very good way
        to store some kinds of data.

            A  word processor would be a good application  for  this
        type  of data structure because you would never need to have
        random access to the data.   In actual practice, this is the
        basic type of storage used for the text in a word  processor
        with one line of text per record.   Actually, a program with
        any degree of sophistication would use a doubly linked list.
        This  would  be  a list with two pointers  per  record,  one
        pointing down to the next record,  and the other pointing up
        to the record just prior to the one in question.  Using this
        kind  of a record structure would allow traversing the  data
        in either direction.




                                  Page 94









                      Chapter 12 - Dynamic Allocation


                           PRINTING THE DATA OUT

             To print the data out, a similar method is used as that
        used to generate the data.  The pointers are initialized and
        are  then  used  to go from record  to  record  reading  and
        displaying   each  record  one  at  a  time.    Printing  is
        terminated when the NULL on the last record is found, so the
        program  doesn't even need to know how many records  are  in
        the list.   Finally, the entire list is deleted to make room
        in  memory  for any additional data that may be  needed,  in
        this case, none.  Care must be taken to assure that the last
        record is not deleted before the NULL is checked.   Once the
        data is gone,  it is impossible to know if you are  finished
        yet.

               MORE ABOUT DYNAMIC ALLOCATION AND LINKED LISTS

             It  is  not difficult,  and it is not trivial,  to  add
        elements into the middle of a linked lists.  It is necessary
        to create the new record,  fill it with data,  and point its
        pointer to the record it is desired to precede.   If the new
        record  is  to be installed between the  3rd  and  4th,  for
        example,  it is necessary for the new record to point to the
        4th record,  and the pointer in the 3rd record must point to
        the new one.  Adding a new record to the beginning or end of
        a  list are each special cases.   Consider what must be done
        to add a new record in a doubly linked list.

             Entire books are written describing different types  of
        linked lists and how to use them,  so no further detail will
        be  given.   The amount of detail given should be sufficient
        for a beginning understanding of C and its capabilities.

                       ANOTHER NEW FUNCTION - CALLOC

             One  more  function must  be  mentioned,  the  "calloc"
        function.   This  function  allocates a block of memory  and
        clears  it  to  all  zeros  which  may  be  useful  in  some
        circumstances.   It is similar to "malloc" and will be  left
        as an exercise for you to read about and use "calloc" if you
        desire.

        PROGRAMMING EXERCISES

        1.  Rewrite the example program STRUCT1.C from chapter 11 to
            dynamically allocate the two structures.

        2.  Rewrite the example program STRUCT2.C from chapter 11 to
            dynamically allocate the 12 structures.




                                  Page 95

TOP

C tutorial Chapter 11 - Structures and Unions
Chapter 11 - Structures and Unions

                                         /* Chapter 11 - Program 1 */
main()
{

struct {
   char initial;    /* last name initial      */
   int age;         /* childs age             */
   int grade;       /* childs grade in school */
   } boy,girl;

   boy.initial = 'R';
   boy.age = 15;
   boy.grade = 75;

   girl.age = boy.age - 1;  /* she is one year younger */
   girl.grade = 82;
   girl.initial = 'H';

   printf("%c is %d years old and got a grade of %d\n",
           girl.initial, girl.age, girl.grade);

   printf("%c is %d years old and got a grade of %d\n",
           boy.initial, boy.age, boy.grade);
}

               
                                   WHAT IS A STRUCTURE?

             A structure is a user defined data type.   You have the
        ability  to  define  a new type of  data  considerably  more
        complex than the types we have been using.  A structure is a
        combination  of  several different previously  defined  data
        types,  including other structures we have defined.  An easy
        to  understand definition is,  a structure is a grouping  of
        related  data in a way convenient to the programmer or  user
        of the program.   The best way to understand a structure  is
        to  look  at  an example,  so if you will load  and  display
        STRUCT1.C, we will do just that.

             The  program begins with a structure  definition.   The
        key  word  "struct"  is followed by  some  simple  variables
        between  the  braces,   which  are  the  components  of  the
        structure.   After  the  closing brace,  you will  find  two
        variables listed,  namely "boy",  and "girl".   According to
        the  definition  of a structure,  "boy" is  now  a  variable
        composed of three elements,  "initial",  "age", and "grade".
        Each of the three fields are associated with "boy", and each
        can  store a variable of its respective type.   The variable
        "girl"  is also a variable containing three fields with  the
        same  names  as those of "boy" but  are  actually  different
        variables.  We have therefore defined 6 simple variables.

                         A SINGLE COMPOUND VARIABLE

             Lets  examine  the  variable "boy"  more  closely.   As
        stated above, each of the three elements of "boy" are simple
        variables  and can be used anywhere in a C program  where  a
        variable of their type can be used.   For example, the "age"
        element  is  an integer variable and can therefore  be  used
        anywhere  in a C program where it is legal to use an integer
        variable,  in calculations, as a counter, in I/O operations,
        etc.   The  only problem we have is defining how to use  the
        simple  variable  "age"  which is a  part  of  the  compound
        variable  "boy".   We  use both names with a  decimal  point
        between  them with the major name first.  Thus "boy.age"  is
        the  complete  variable name for the "age" field  of  "boy".
        This  construct can be used anywhere in a C program that  it
        is desired to refer to this field.   In fact,  it is illegal
        to  use the name "boy" or "age" alone because they are  only
        partial definitions of the complete field.  Alone, the names
        refer to nothing.

                     ASSIGNING VALUES TO THE VARIABLES

             Using  the above definition,  we can assign a value  to
        each  of  the  three fields of "boy" and each of  the  three
        fields  of  "girl".   Note carefully that  "boy.initial"  is


                                  Page 77









                      Chapter 11 - Structures and Unions


        actually  a "char" type variable,  because it  was  assigned
        that in the structure, so it must be assigned a character of
        data.   Notice  that "boy.initial" is assigned the character
        'R'  in agreement with the above rules.   The remaining  two
        fields of "boy" are assigned values in accordance with their
        respective  types.   Finally  the three fields of  girl  are
        assigned values but in a different order to illustrate  that
        the order of assignment is not critical.

                     HOW DO WE USE THE RESULTING DATA?

             Now  that  we  have assigned values to the  six  simple
        variables, we can do anything we desire with them.  In order
        to keep this first example simple,  we will simply print out
        the values to see if they really do exist as  assigned.   If
        you carefully inspect the "printf" statements,  you will see
        that there is nothing special about them.  The compound name
        of each variable is specified because that is the only valid
        name by which we can refer to these variables.

             Structures  are  a very useful method of grouping  data
        together  in  order to make a program easier  to  write  and
        understand.   This  first example is too simple to give  you
        even  a hint of the value of using structures,  but continue
        on  through  these lessons and eventually you will  see  the
        value of using structures.

             Compile and run STRUCT1.C and observe the output.

                           AN ARRAY OF STRUCTURES

                                        /* Chapter 11 - Program 2 */
main()
{
struct {
   char initial;
   int age;
   int grade;
   } kids[12];

int index;

   for (index = 0;index < 12;index++) {
      kids[index].initial = 'A' + index;
      kids[index].age = 16;
      kids[index].grade = 84;
   }

   kids[3].age = kids[5].age = 17;
   kids[2].grade = kids[6].grade = 92;
   kids[4].grade = 57;

   kids[10] = kids[4];               /* Structure assignment  */

   for (index = 0;index < 12;index++)
      printf("%c is %d years old and got a grade of %d\n",
             kids[index].initial, kids[index].age,
             kids[index].grade);
}
                           
                           
             Load  and  display the next  program  named  STRUCT2.C.
        This  program  contains  the same  structure  definition  as
        before  but  this  time we define an array of  12  variables
        named "kids".   This program therefore contains 12 times 3 =
        36  simple variables,  each of which can store one  item  of
        data  provided  that  it is of the correct  type.   We  also
        define a simple variable named "index" for use in the  "for"
        loops.

             In order to assign each of the fields a value, we use a
        "for"  loop  and  each  pass through  the  loop  results  in
        assigning a value to three of the fields.  One pass  through
        the  loop assigns all of the values for one of  the  "kids".
        This would not be a very useful way to assign data in a real
        situation, but a loop could read the data in from a file and
        store it in the correct fields.  You might consider this the
        crude beginning of a data base, which it is.

             In  the next few instructions of the program we  assign
        new  values to some of the fields to illustrate  the  method


                                  Page 78









                      Chapter 11 - Structures and Unions


        used to accomplish this.  It should be self explanatory,  so
        no additional comments will be given.

                       A RECENT UPGRADE TO THE C LANGUAGE

             Most  modern  C  compilers will allow you  to  copy  an
        entire structure with one statement. This is a fairly recent
        addition  to the C language and will be a part of  the  ANSI
        standard  when it is published, so you should feel  free  to
        use it with your C compiler if it is available.  Line 22  is
        an  example  of  using  a  structure  assignment.   In  this
        statement,  all  3 fields of kids[4] are copied  into  their
        respective fields of kids[10].

                   WE FINALLY DISPLAY ALL OF THE RESULTS

             The  last few statements contain a "for" loop in  which
        all  of  the generated values are displayed in  a  formatted
        list.   Compile and run the program to see if it  does  what
        you expect it to do.

                   USING POINTERS AND STRUCTURES TOGETHER

                                                            /* Chapter 11 - Program 3 */
main()
{
struct {
   char initial;
   int age;
   int grade;
   } kids[12],*point,extra;

int index;

   for (index = 0;index < 12;index++) {
      point = kids + index;
      point->initial = 'A' + index;
      point->age = 16;
      point->grade = 84;
   }

   kids[3].age = kids[5].age = 17;
   kids[2].grade = kids[6].grade = 92;
   kids[4].grade = 57;

   for (index = 0;index < 12;index++) {
      point = kids + index;
      printf("%c is %d years old and got a grade of %d\n",
             (*point).initial, kids[index].age,
             point->grade);
   }
   extra = kids[2];               /* Structure assignment */
   extra = *point;                /* Structure assignment */
}

             Load  and  display  the  file named  STRUCT3.C  for  an
        example of using pointers with structures.   This program is
        identical  to the last program except that it uses  pointers
        for some of the operations.

             The  first  difference shows up in  the  definition  of
        variables  following  the  structure  definition.   In  this
        program  we define a pointer named "point" which is  defined
        as  a  pointer that points to the structure.   It  would  be
        illegal  to  try to use this pointer to point to  any  other
        variable  type.   There  is a very definite reason for  this
        restriction  in  C as we have alluded to  earlier  and  will
        review in the next few paragraphs.

             The  next difference is in the "for" loop where we  use
        the pointer for accessing the data fields.  Since "kids"  is
        a  pointer  variable that points to the  structure,  we  can
        define "point" in terms of "kids".  The variable "kids" is a
        constant so it cannot be changed in value, but "point" is  a
        pointer  variable and can be assigned any  value  consistent
        with  its being required to point to the structure.   If  we
        assign  the  value of "kids" to "point" then  it  should  be
        clear that it will point to the first element of the  array,
        a structure containing three fields.






                                  Page 79









                      Chapter 11 - Structures and Unions


                             POINTER ARITHMETIC

             Adding  1 to "point" will now cause it to point to  the
        second  field of the array because of the way  pointers  are
        handled in C.   The system knows that the structure contains
        three  variables  and it knows how many memory elements  are
        required to store the complete structure.   Therefore if  we
        tell it to add one to the pointer,  it will actually add the
        number  of  memory  elements  required to get  to  the  next
        element of the array.   If, for example, we were to add 4 to
        the  pointer,  it would advance the value of the  pointer  4
        times the size of the structure,  resulting in it pointing 4
        elements  farther  along the array.   This is the  reason  a
        pointer  cannot be used to point to any data type other than
        the one for which it was defined.

             Now to return to the program displayed on your monitor.
        It  should be clear from the previous discussion that as  we
        go through the loop, the pointer will point to the beginning
        of  one of the array elements each time.   We can  therefore
        use  the  pointer to reference the various elements  of  the
        structure.   Referring to the elements of a structure with a
        pointer occurs so often in C that a special method of  doing
        that  was  devised.   Using "point->initial" is the same  as
        using  "(*point).initial" which is really the way we did  it
        in  the  last  two programs.  Remember that  *point  is  the
        stored  data to which the pointer points and  the  construct
        should be clear.  The "->" is made up of the minus sign  and
        the greater than sign.

             Since the pointer points to the structure, we must once
        again  define which of the elements we wish to refer to each
        time  we use one of the elements of  the  structure.   There
        are, as we have seen, several different methods of referring
        to the members of the structure, and in the "for" loop  used
        for output at the end of the program, we use three different
        methods.   This  would be considered very  poor  programming
        practice,  but  is done this way here to illustrate  to  you
        that  they all lead to the same result.  This  program  will
        probably   require  some  study  on  your  part   to   fully
        understand,  but  it will be worth your time and  effort  to
        grasp these principles.

             Lines  29  and  30  are  two  additional  examples   of
        structure assignment for your benefit.

             Compile and run this program.






                                  Page 80









                      Chapter 11 - Structures and Unions


                        NESTED AND NAMED STRUCTURES

                                     /* Chapter 11 - Program 4 */
main()
{
struct person {
   char name[25];
   int age;
   char status;        /* M = married, S = single */
} ;

struct alldat {
   int grade;
   struct person descrip;
   char lunch[25];
} student[53];

struct alldat teacher,sub;

teacher.grade = 94;
teacher.descrip.age = 34;
teacher.descrip.status = 'M';
strcpy(teacher.descrip.name,"Mary Smith");
strcpy(teacher.lunch,"Baloney sandwich");

sub.descrip.age = 87;
sub.descrip.status = 'M';
strcpy(sub.descrip.name,"Old Lady Brown");
sub.grade = 73;
strcpy(sub.lunch,"Yogurt and toast");

student[1].descrip.age = 15;
student[1].descrip.status = 'S';
strcpy(student[1].descrip.name,"Billy Boston");
strcpy(student[1].lunch,"Peanut Butter");
student[1].grade = 77;

student[7].descrip.age = 14;
student[12].grade = 87;

}
                        
                        
             Load and display the file named NESTED.C for an example
        of  a nested structure.   The structures we have seen so far
        have been very simple,  although useful.   It is possible to
        define  structures  containing dozens and even  hundreds  or
        thousands  of  elements but it would be to  the  programmers
        advantage not to define all of the elements at one pass  but
        rather to use a hierarchical structure of definition.   This
        will be illustrated with the program on your monitor.

             The  first  structure  contains three elements  but  is
        followed by no variable name.  We therefore have not defined
        any variables only a structure, but since we have included a
        name  at the beginning of the structure,  the  structure  is
        named  "person".   The name "person" can be used to refer to
        the  structure  but not to any variable  of  this  structure
        type.   It is therefore a new type that we have defined, and
        we can use the new type in nearly the same way we use "int",
        "char",  or  any  other  types that exist in  C.   The  only
        restriction is that this new name must always be  associated
        with the reserved word "struct".

             The  next  structure definition contains  three  fields
        with the middle field being the previously defined structure
        which we named "person".  The variable which has the type of
        "person" is named "descrip".   So the new structure contains
        two   simple   variables,   "grade"  and  a   string   named
        "lunch[25]",  and  the  structure  named  "descrip".   Since
        "descrip"  contains  three  variables,   the  new  structure
        actually contains 5 variables.  This structure is also given
        a name "alldat",  which is another type definition.  Finally
        we  define an array of 53 variables each with the  structure
        defined by "alldat",  and each with the name "student".   If
        that is clear,  you will see that we have defined a total of
        53 times 5 variables,  each of which is capable of storing a
        value.

                             TWO MORE VARIABLES

             Since  we  have a new type definition we can use it  to
        define  two  more variables.   The variables  "teacher"  and
        "sub"  are  defined in line 16 to be variables of  the  type
        "alldat",  so  that each of these two  variables  contain  5
        fields which can store data.

                       NOW TO USE SOME OF THE FIELDS

             In  the next five lines of the program,  we will assign
        values to each of the fields of "teacher".   The first field
        is  the  "grade" field and is handled just  like  the  other


                                  Page 81









                      Chapter 11 - Structures and Unions


        structures  we  have studied because it is not part  of  the
        nested structure.  Next we wish to assign a value to her age
        which  is  part of the nested structure.   To  address  this
        field  we start with the variable name "teacher" to which we
        append  the name of the group "descrip",  and then  we  must
        define which field of the nested structure we are interested
        in,  so  we append the name "age".   The teachers status  is
        handled in exactly the same manner as her age,  but the last
        two  fields  are  assigned  strings using  the  string  copy
        "strcpy" function which must be used for string  assignment.
        Notice  that the variable names in the "strcpy" function are
        still variable names even though they are made up of several
        parts each.

             The variable "sub" is assigned nonsense values in  much
        the  same  way,  but in a different order since they do  not
        have to occur in any required order.   Finally, a few of the
        "student"  variables  are assigned values  for  illustrative
        purposes  and  the program ends.   None of  the  values  are
        printed  for illustration since several were printed in  the
        last examples.

             Compile and run this program, but when you run it,  you
        may  get a "stack overflow" error.  C uses its own  internal
        stack  to  store  the automatic variables  on,  but  most  C
        compilers  use  only a 2048 byte stack as a  default.   This
        program  requires more than that for the defined  structures
        so it will be necessary for you to increase the stack  size.
        The  method of doing this for some of compilers is given  in
        the  accompanying  "COMPILR.DOC"  file  included  with  this
        tutorial.   Consult your compiler documentation for  details
        concerning  your compiler if yours is not listed.  There  is
        another  way  around this problem, and that is to  move  the
        structure  and variable definitions outside of  the  program
        where they will be external variables and therefore  static.
        The  result  is that they will not be kept on  the  internal
        stack and the stack will not overflow.  It would be good for
        you to try both methods of fixing this problem.

                           MORE ABOUT STRUCTURES

             It is possible to continue nesting structures until you
        get  totally confused.   If you define  them  properly,  the
        computer  will  not get confused because there is no  stated
        limit as to how many levels of nesting are  allowed.   There
        is probably a practical limit of three beyond which you will
        get confused, but the language has no limit.  In addition to
        nesting, you can include as many structures as you desire in
        any level of structures,  such as defining another structure
        prior  to  "alldat" and using it in "alldat" in addition  to
        using  "person".   The  structure named  "person"  could  be


                                  Page 82









                      Chapter 11 - Structures and Unions


        included in "alldat" two or more times if desired,  as could
        pointers to it.

             Structures can contain arrays of other structures which
        in  turn  can  contain  arrays  of  simple  types  or  other
        structures.   It  can go on and on until you lose all reason
        to  continue.   I am only trying to illustrate to  you  that
        structures  are  very valuable and you will find them  great
        aids to programming if you use them wisely.  Be conservative
        at first, and get bolder as you gain experience.

             More  complex structures will not be illustrated  here,
        but  you will find examples of additional structures in  the
        example  programs  included  in the  last  chapter  of  this
        tutorial.     For   example,   see   the   "#include"   file
        "STRUCT.DEF".
        
/* *************** structure definitions ******************** */
struct vars{            /* variable storage                   */
   char varname[7];     /* variable name A-F & I-N            */
   char outtype;        /* output format for variable         */
   double value;        /* value of the variable              */
   };                   /*                                    */
                        /*                                    */
struct lines{           /* dynamic structure for transcripts  */
   struct lines *dn;    /* next transcript line               */
   struct lines *up;    /* last transcript line               */
   char *lineloc;       /* point to dynamic location of line  */
   int linelngt;        /* length of line stored here         */
   char isvalue;        /* 1 = calculated value, 0 = none     */
   char marked;         /* 1 = line marked, 0 = not marked    */
   char strval[13];     /* string representation of variable  */
   };                   /*                                    */
/* ************** end of structure definitions ************** */
        
        

                              WHAT ARE UNIONS?

                                         /* Chapter 11 - Program 5 */
main()
{
union {
   int value;     /* This is the first part of the union */
   struct {
      char first;   /* These two values are the second     */
      char second;
   } half;
} number;

long index;

   for (index = 12;index < 300000;index += 35231) {
      number.value = index;
      printf("%8x %6x %6x\n",number.value, number.half.first,
              number.half.second);
   }
}
                              
                              
             Load the file named UNION1.C for an example of a union.
        Simply stated,  a union allows you a way to look at the same
        data  with  different types,  or to use the same  data  with
        different names. Examine the program on your monitor.

             In this example we have two elements to the union,  the
        first part being the integer named "value",  which is stored
        as  a  two byte variable somewhere in the computers  memory.
        The  second  element is made up of two  character  variables
        named "first" and "second".   These two variables are stored
        in  the  same storage locations that "value" is  stored  in,
        because  that is what a union does.   A union allows you  to
        store  different types of data in the same physical  storage
        locations.  In this case, you could put an integer number in
        "value",  then retrieve it in its two halves by getting each
        half  using  the  two  names  "first"  and  "second".   This
        technique is often used to pack data bytes together when you
        are,  for  example,  combining  bytes  to  be  used  in  the
        registers of the microprocessor.

             Accessing  the fields of the union are very similar  to
        accessing the fields of a structure and will be left to  you
        to determine by studying the example.

             One  additional  note  must be  given  here  about  the
        program.   When it is run using some C compilers,  the  data
        will   be  displayed  with  two  leading  f's  due  to   the
        hexadecimal output promoting the char type variables to  int
        and extending the sign bit to the left.  Converting the char
        type data fields to int type fields prior to display  should
        remove the leading f's from your display.  This will involve
        defining  two new int type variables and assigning the  char


                                  Page 83









                      Chapter 11 - Structures and Unions


        type  variables to them.  This will be left as  an  exercise
        for  you.  Note that the same problem will come up in a  few
        of the later files also.

             Compile and run this program and observe that the  data
        is  read out as an "int" and as two "char"  variables.   The
        "char" variables are reversed in order because of the way an
        "int" variable is stored internally in your computer.  Don't
        worry  about this.  It is not a problem but it can be a very
        interesting area of study if you are so inclined.

                           ANOTHER UNION EXAMPLE

                                                                    /* Chapter 11 - Program 6 */
#define AUTO 1
#define BOAT 2
#define PLANE 3
#define SHIP 4

main()
{
struct automobile {  /* structure for an automobile         */
   int tires;
   int fenders;
   int doors;
};

typedef struct {     /* structure for a boat or ship        */
   int displacement;
   char length;
} BOATDEF;

struct {
   char vehicle;         /* what type of vehicle?           */
   int weight;           /* gross weight of vehicle         */
   union {               /* type-dependent data             */
      struct automobile car;      /* part 1 of the union    */
      BOATDEF boat;               /* part 2 of the union    */
      struct {
         char engines;
         int wingspan;
      } airplane;                 /* part 3 of the union    */
      BOATDEF ship;               /* part 4 of the union    */
   } vehicle_type;
   int value;            /* value of vehicle in dollars     */
   char owner[32];       /* owners name                     */
} ford, sun_fish, piper_cub;   /* three variable structures */

       /* define a few of the fields as an illustration     */

   ford.vehicle = AUTO;
   ford.weight = 2742;              /* with a full gas tank */
   ford.vehicle_type.car.tires = 5;  /* including the spare */
   ford.vehicle_type.car.doors = 2;

   sun_fish.value = 3742;           /* trailer not included */
   sun_fish.vehicle_type.boat.length = 20;

   piper_cub.vehicle = PLANE;
   piper_cub.vehicle_type.airplane.wingspan = 27;

   if (ford.vehicle == AUTO) /* which it is in this case */
      printf("The ford has %d tires.\n",ford.vehicle_type.car.tires);

   if (piper_cub.vehicle == AUTO) /* which it is not in this case */
      printf("The plane has %d tires.\n",piper_cub.vehicle_type.
             car.tires);
}

                           
             Load  and  display the file named UNION2.C for  another
        example of a union,  one which is much more common.  Suppose
        you  wished to build a large database including  information
        on many types of vehicles.  It would be silly to include the
        number of propellers on a car,  or the number of tires on  a
        boat.   In  order to keep all pertinent data,  however,  you
        would  need  those  data points for their  proper  types  of
        vehicles.   In  order to build an efficient data  base,  you
        would need several different types of data for each vehicle,
        some  of which would be common,  and some of which would  be
        different.  That is exactly what we are doing in the example
        program on your monitor.

             In this program,  we will define a complete  structure,
        then  decide which of the various types can go into it.   We
        will  start at the top and work our  way  down.   First,  we
        define  a  few constants with the #defines,  and  begin  the
        program  itself.   We define a structure named  "automobile"
        containing  several fields which you should have no  trouble
        recognizing, but we define no variables at this time.

                         A NEW CONCEPT, THE TYPEDEF

             Next  we  define a new type of data with  a  "typedef".
        This  defines  a complete new type that can be used  in  the
        same way that "int" or "char" can be used.   Notice that the
        structure  has  no name,  but at the end where  there  would
        normally be a variable name there is the name "BOATDEF".  We
        now have a new type, "BOATDEF", that can be used to define a
        structure anyplace we would like to.   Notice that this does
        not  define  any  variables,  only a  new  type  definition.
        Capitalizing  the name is a personal preference only and  is
        not  a  C standard.   It makes the "typedef" look  different
        from a variable name.

             We  finally come to the big structure that defines  our
        data using the building blocks already defined  above.   The
        structure is composed of 5 parts, two simple variables named


                                  Page 84









                      Chapter 11 - Structures and Unions


        "vehicle" and "weight",  followed by the union,  and finally
        the last two simple variables named "value" and "owner".  Of
        course  the union is what we need to look at carefully here,
        so focus on it for the moment.   You will notice that it  is
        composed  of four parts,  the first part being the  variable
        "car" which is a structure that we defined previously.   The
        second  part is a variable named "boat" which is a structure
        of the type "BOATDEF" previously defined.  The third part of
        the  union is the variable "airplane" which is  a  structure
        defined in place in the union.   Finally we come to the last
        part  of  the  union,  the variable named  "ship"  which  is
        another structure of the type "BOATDEF".

             I  hope  it is obvious to you that all four could  have
        been defined in any of the three ways shown,  but the  three
        different  methods  were used to show you that any could  be
        used.   In practice,  the clearest definition would probably
        have occurred by using the "typedef" for each of the parts.

                            WHAT DO WE HAVE NOW?

             We  now have a structure that can be used to store  any
        of  four different kinds of data structures.   The  size  of
        every  record will be the size of that record containing the
        largest  union.   In this case part 1 is the  largest  union
        because  it is composed of three integers,  the others being
        composed  of  an integer and a character  each.   The  first
        member  of this union would therefore determine the size  of
        all structures of this type.  The resulting structure can be
        used to store any of the four types of data, but it is up to
        the  programmer  to  keep track of what is  stored  in  each
        variable of this type.   The variable "vehicle" was designed
        into  this  structure to keep track of the type  of  vehicle
        stored here.   The four defines at the top of the page  were
        designed  to  be  used  as indicators to be  stored  in  the
        variable "vehicle".

             A  few  examples of how to use the resulting  structure
        are given in the next few lines of the program.  Some of the
        variables are defined and a few of them are printed out  for
        illustrative purposes.

             The union is not used too frequently,  and almost never
        by   beginning   programmers.    You   will   encounter   it
        occasionally  so  it is worth your effort to at  least  know
        what  it is.   You do not need to know the details of it  at
        this time,  so don't spend too much time studying it.   When
        you do have a need for a variant structure, a union, you can
        learn it at that time. For your own benefit, however, do not
        slight the structure. You should use the structure often.



                                  Page 85









                      Chapter 11 - Structures and Unions


                            WHAT IS A BITFIELD?

                                         /* Chapter 11 - Program 7 */
main()
{
union {
   int index;
   struct {
      unsigned int x : 1;
      unsigned int y : 2;
      unsigned int z : 2;
   } bits;
} number;

   for (number.index = 0;number.index < 20;number.index++) {
      printf("index = %3d, bits = %3d%3d%3d\n",number.index,
              number.bits.z,number.bits.y,number.bits.x);
   }
}
                           
                           
                           
             Load  and display the program named BITFIELD.C  for  an
        example  of how to define and use a bitfield,  a  relatively
        new  addition  to  the  programming  language  C.   In  this
        program,  we  have a union made up of a  single  "int"  type
        variable  in  line 5 and the structure defined  in  lines  6
        through  10.  The structure is composed of  three  bitfields
        named "x", "y", and "z".  The variable named "x" is only one
        bit wide, the variable "y" is two bits wide and adjacent  to
        the variable "x", and the variable "z" is two bits wide  and
        adjacent  to  "y".  Moreover, because the union  causes  the
        bits  to  be  stored  in the same  memory  location  as  the
        variable "index", the variable "x" is the least  significant
        bit  of the variable "index", "y" is the next two bits,  and
        "z" is the next two.

             Compile  and run the program and you will see  that  as
        the variable "index" is incremented by 1 each time you  will
        see  the  bitfields  of  the union  counting  due  to  their
        respective locations within the "int" definition.

             One  thing must be pointed out, the bitfields  must  be
        defined as parts of an "unsigned int" or your compiler  will
        issue an error message.

                       WHAT IS THE BITFIELD GOOD FOR?

             The  bitfield is very useful if you have a lot of  data
        to  separate  into separate bits or groups  of  bits.   Many
        systems use some sort of a packed format to get lots of data
        stored in a few  bytes.   Your  imagination  is  your   only
        limitation to use of this feature of C.


        PROGRAMMING EXERCISES

        1.   Define a named structure  containing a string field for
             a name,  an integer for feet, and another for arms. Use
             the new type to define an array of about 6 items.  Fill
             the fields with data and print them out as follows.

             A human being has 2 legs and 2 arms.
             A dog has 4 legs and 0 arms.
             A television set has 4 legs and 0 arms.
             A chair has 4 legs and 2 arms.
             etc.

        2.   Rewrite  exercise 1 using  a pointer to print the  data
             out.



                                  Page 86

TOP

C tutorial Chapter 10 - File Input/Output
Chapter 10 - File Input/Output


                              OUTPUT TO A FILE

                                       /* Chapter 10 - Program 1 */
#include "stdio.h"
main()
{
FILE *fp;
char stuff[25];
int index;

   fp = fopen("TENLINES.TXT","w");   /* open for writing */
   strcpy(stuff,"This is an example line.");

   for (index = 1;index <= 10;index++)
      fprintf(fp,"%s  Line number %d\n",stuff,index);

   fclose(fp);    /* close the file before ending program */
}                              
                              
             Load  and  display  the file named FORMOUT.C  for  your
        first example of writing data to a file.  We begin as before
        with the "include" statement for "stdio.h", then define some
        variables for use in the example including a rather  strange
        looking new type.

             The  type  "FILE"  is used for a file variable  and  is
        defined in the "stdio.h" file.   It is used to define a file
        pointer  for use in file operations.   The definition  of  C
        contains  the requirement for a pointer to a "FILE",  and as
        usual, the name can be any valid variable name.

                               OPENING A FILE

             Before we can write to a file,  we must open it.   What
        this  really means is that we must tell the system  that  we
        want  to  write to a file and what the filename is.   We  do
        this with the "fopen" function illustrated in the first line
        of the program.   The file pointer, "fp" in our case, points
        to   the  file  and  two  arguments  are  required  in   the
        parentheses,  the filename first, followed by the file type.
        The filename is any valid DOS filename, and can be expressed
        in  upper  or lower case letters,  or even mixed if  you  so
        desire.   It is enclosed in double quotes.  For this example
        we have chosen the name TENLINES.TXT.   This file should not
        exist  on your disk at this time.   If you have a file  with
        this  name,  you should change its name or move  it  because
        when  we execute this program,  its contents will be erased.
        If you don't have a file by this name,  that is good because
        we will create one and put some data into it.

                               READING ("r")

             The  second parameter is the file attribute and can  be
        any  of three letters, "r", "w", or "a", and must  be  lower
        case. There are actually additional attributes available  in
        C to allow more flexible I/O.  When an "r" is used, the file
        is  opened for reading, a "w" is used to indicate a file  to
        be used for writing, and an "a" indicates that you desire to
        append  additional data to the data already in  an  existing
        file.   Opening  a file for reading requires that  the  file
        already exist.  If it does not exist, the file pointer  will
        be set to NULL and can be checked by the program.

                               WRITING ("w")

             When  a file is opened for writing,  it will be created
        if it does not already exist and it will be reset if it does
        resulting in deletion of any data already there.


                                   Page 70









                       Chapter 10 - File Input/Output



                              APPENDING ("a")

             When a file is opened for appending, it will be created
        if it does not already exist and it will be initially empty.
        If  it does exist,  the data input point will be the end  of
        the  present data so that any new data will be added to  any
        data that already exists in the file.

                           OUTPUTTING TO THE FILE

             The  job of actually outputting to the file  is  nearly
        identical  to  the  outputting we have already done  to  the
        standard output device.   The only real differences are  the
        new  function names and the addition of the file pointer  as
        one  of  the function arguments.   In the  example  program,
        "fprintf" replaces our familiar "printf" function name,  and
        the  file  pointer  defined earlier is  the  first  argument
        within  the  parentheses.   The remainder of  the  statement
        looks  like,  and  in  fact is identical  to,  the  "printf"
        statement.

                               CLOSING A FILE

             To close a file,  you simply use the function  "fclose"
        with the file pointer in the parentheses.  Actually, in this
        simple  program,  it  is  not necessary to  close  the  file
        because   the  system  will  close  all  open  files  before
        returning to DOS.  It would be good programming practice for
        you to get in the habit of closing all files in spite of the
        fact  that they will be closed automatically,  because  that
        would act as a reminder to you of what files are open at the
        end of each program.

             You can open a file for writing,  close it,  and reopen
        it  for  reading,  then  close it,  and open  it  again  for
        appending,  etc.   Each time you open it,  you could use the
        same file pointer,  or you could use a different  one.   The
        file  pointer  is simply a tool that you use to point  to  a
        file and you decide what file it will point to.

             Compile  and run this program.   When you run  it,  you
        will  not  get any output to the monitor because it  doesn't
        generate any.   After running it, look at your directory for
        a file named TENLINES.TXT and "type" it.  That is where your
        output will be.   Compare the output with that specified  in
        the program.  It should agree.

             Do not erase the file named TENLINES.TXT yet.   We will
        use it in some of the other examples in this chapter.



                                   Page 71









                       Chapter 10 - File Input/Output



                  OUTPUTTING A SINGLE CHARACTER AT A TIME
                  
                                         /* Chapter 10 - Program 2 */
#include "stdio.h"
main()
{
FILE *point;
char others[35];
int indexer,count;

   strcpy(others,"Additional lines.");
   point = fopen("tenlines.txt","a"); /* open for appending */

   for (count = 1;count <= 10;count++) {
      for (indexer = 0;others[indexer];indexer++)
         putc(others[indexer],point);  /* output a single character */
      putc('\n',point);                /* output a linefeed */
   }
   fclose(point);
}
                  

             Load the next example file,  CHAROUT.C,  and display it
        on your monitor.  This program will illustrate how to output
        a single character at a time.

             The  program begins with the "include" statement,  then
        defines  some variables including a file pointer.   We  have
        called the file pointer "point" this time, but we could have
        used any other valid variable name.  We then define a string
        of characters to use in the output function using a "strcpy"
        function.   We are ready to open the file for appending  and
        we  do so in the "fopen" function,  except this time we  use
        the  lower cases for the filename.   This is done simply  to
        illustrate  that  DOS  doesn't care about the  case  of  the
        filename.  Notice that the file will be opened for appending
        so  we  will  add  to the lines  inserted  during  the  last
        program.

             The  program is actually two nested "for"  loops.   The
        outer  loop  is  simply a count to ten so that  we  will  go
        through the inner loop ten times.   The inner loop calls the
        function  "putc" repeatedly until a character in "others" is
        detected to be a zero.

                            THE "putc" FUNCTION

             The  part  of the program we are interested in  is  the
        "putc" function.   It outputs one character at a  time,  the
        character  being  the first argument in the parentheses  and
        the  file pointer being the second and last  argument.   Why
        the  designer of C made the pointer first in  the  "fprintf"
        function, and last in the "putc" function is a good question
        for which there may be no answer.   It seems like this would
        have been a good place to have used some consistency.

             When  the textline "others" is exhausted,  a newline is
        needed because a newline was not included in the  definition
        above.   A  single "putc" is then executed which outputs the
        "\n" character to return the carriage and do a linefeed.

             When  the outer loop has been executed ten  times,  the
        program  closes the file and terminates.   Compile  and  run
        this  program but once again there will be no output to  the
        monitor.

             Following  execution  of the program,  "type" the  file
        named  TENLINES.TXT and you will see that the 10  new  lines
        were  added to the end of the 10 that already  existed.   If
        you run it again,  yet another 10 lines will be added.  Once


                                   Page 72









                       Chapter 10 - File Input/Output


        again,  do  not  erase  this file because we are  still  not
        finished with it.

                               READING A FILE

                                         /* Chapter 10 - Program 3 */
#include "stdio.h"

main()
{
FILE *funny;
char c;

   funny = fopen("TENLINES.TXT","r");

   if (funny == NULL) printf("File doesn't exist\n");
   else {
      do {
         c = getc(funny);    /* get one character from the file */
         putchar(c);         /* display it on the monitor       */
      } while (c != EOF);    /* repeat until EOF (end of file)  */
   }
   fclose(funny);
}
                              
                              
             Load  the file named READCHAR.C and display it on  your
        monitor. This is our first program to read a file.

             This program begins with the familiar  "include",  some
        data  definitions,  and  the  file opening  statement  which
        should  require no explanation except for the fact  that  an
        "r"  is  used  here because we want to  read  it.   In  this
        program,  we  check to see that the file exists,  and if  it
        does,  we  execute  the  main body of the  program.   If  it
        doesn't,  we print a message and quit.  If the file does not
        exist,  the system will set the pointer equal to NULL  which
        we can test.

             The  main body of the program is one "do while" loop in
        which a single character is read from the file and output to
        the monitor until an EOF (end of file) is detected from  the
        input  file.   The  file is then closed and the  program  is
        terminated.

                         CAUTION  CAUTION  CAUTION

             At  this  point, we have the potential for one  of  the
        most  common and most perplexing problems of programming  in
        C.   The  variable returned from the "getc"  function  is  a
        character, so we can use a "char" variable for this purpose.
        There is a problem that could develop here if we happened to
        use  an "unsigned char" however, because C returns  a  minus
        one for an EOF which an "unsigned char" type variable is not
        capable of containing.  An "unsigned char" type variable can
        only have the values of zero to 255, so it will return a 255
        for a minus one.  This is a very frustrating problem to  try
        to  find.   The  program can never find  the  EOF  and  will
        therefore  never  terminate  the  loop.   This  is  easy  to
        prevent,  always  use  an "char" type variable  for  use  in
        returning an EOF.

             There is another problem with this program but we  will
        worry  about it when we get to the next program and solve it
        with the one following that.

             After  you  compile  and  run  this  program  and   are
        satisfied  with the results,  it would be a good exercise to
        change the name of "TENLINES.TXT" and run the program  again
        to see that the NULL test actually works as stated.  Be sure
        to  change  the name back because we are still not  finished
        with "TENLINES.TXT".


                                   Page 73









                       Chapter 10 - File Input/Output



                          READING A WORD AT A TIME

                                         /* Chapter 10 - Program 4 */
#include "stdio.h"

main()
{
FILE *fp1;
char oneword[100];
char c;

   fp1 = fopen("TENLINES.TXT","r");

   do {
      c = fscanf(fp1,"%s",oneword); /* got one word from the file */
      printf("%s\n",oneword);       /* display it on the monitor  */
   } while (c != EOF);              /* repeat until EOF           */

   fclose(fp1);
}
                          
                          
             Load  and  display  the file named  READTEXT.C  for  an
        example of how to read a word at a time.

             This  program  is nearly identical as the  last  except
        that  this program uses the "fscanf" function to read  in  a
        string  at  a  time.   Because the "fscanf"  function  stops
        reading  when it finds a space or a  newline  character,  it
        will read a word at a time, and display the results one word
        to  a line.   You will see this when you compile and run it,
        but first we must examine a programming problem.

                             THIS IS A PROBLEM

                                        /* Chapter 10 - Program 5 */
#include "stdio.h"

main()
{
FILE *fp1;
char oneword[100];
char c;

   fp1 = fopen("TENLINES.TXT","r");

   do {
      c = fscanf(fp1,"%s",oneword); /* got one word from the file */
      if (c != EOF)
         printf("%s\n",oneword);    /* display it on the monitor  */
   } while (c != EOF);              /* repeat until EOF           */

   fclose(fp1);
}
                             
             Inspection of the program will reveal that when we read
        data in and detect the EOF, we print out something before we
        check  for the EOF resulting in an extra line  of  printout.
        What  we usually print out is the same thing printed on  the
        prior  pass  through  the loop because it is  still  in  the
        buffer "oneword".  We therefore must check for EOF before we
        execute  the  "printf"  function.   This has  been  done  in
        READGOOD.C,  which  you will shortly examine,  compile,  and
        execute.

             Compile  and execute the original program we have  been
        studying, READTEXT.C and observe the output.  If you haven't
        changed  TENLINES.TXT you will end up with "Additional"  and
        "lines."  on  two  separate lines  with  an  extra  "lines."
        displayed  because of the "printf" before checking for  EOF.
        Note  that some compilers apparently clear the buffer  after
        printing  so you may get an extra blank line instead of  two
        lines with "lines." on them.

             Compile  and  execute READGOOD.C and observe  that  the
        extra  "lines." does not get displayed because of the  extra
        check for the EOF in the middle of the loop.   This was also
        the problem referred to when we looked at READCHAR.C,  but I
        chose  not  to expound on it there because the error in  the
        output was not so obvious.

                        FINALLY, WE READ A FULL LINE

                        
                                        /* Chapter 10 - Program 6 */
#include "stdio.h"

main()
{
FILE *fp1;
char oneword[100];
char *c;

   fp1 = fopen("TENLINES.TXT","r");

   do {
      c = fgets(oneword,100,fp1); /* get one line from the file */
      if (c != NULL)
         printf("%s",oneword);    /* display it on the monitor  */
   } while (c != NULL);          /* repeat until NULL          */

   fclose(fp1);
}
                        
             Load and display the file READLINE.C for an example  of
        reading  a complete line.   This program is very similar  to
        those  we have been studying except that we read a  complete
        line.

             We  are  using "fgets" which reads in an  entire  line,
        including  the newline character into a buffer.   The buffer


                                   Page 74









                       Chapter 10 - File Input/Output


        to be read into is the first argument in the function  call,
        and  the maximum number of characters to read is the  second
        argument,  followed by the file pointer.  This function will
        read  characters into the input buffer until it either finds
        a  newline  character,  or it reads the  maximum  number  of
        characters  allowed minus one.   It leaves one character for
        the end of string NULL character.   In addition, if it finds
        an  EOF,  it will return a value of NULL.   In our  example,
        when the EOF is found,  the pointer "c" will be assigned the
        value  of NULL.   NULL is defined as zero in your  "stdio.h"
        file.

             When  we find that "c" has been assigned the  value  of
        NULL,  we can stop processing data, but we must check before
        we print just like in the last program.

             Last of course, we close the file.

                       HOW TO USE A VARIABLE FILENAME

                                         /* Chapter 10 - Program 7 */
#include "stdio.h"

main()
{
FILE *fp1;
char oneword[100],filename[25];
char *c;

   printf("Enter filename -> ");
   scanf("%s",filename);         /* read the desired filename */
   fp1 = fopen(filename,"r");

   do {
      c = fgets(oneword,100,fp1); /* get one line from the file */
      if (c != NULL)
         printf("%s",oneword);    /* display it on the monitor  */
   } while (c != NULL);          /* repeat until NULL          */

   fclose(fp1);
}

                       
                       
             Load  and display the file ANYFILE.C for an example  of
        reading  from any file.   This program asks the user for the
        filename desired,  reads in the filename and opens that file
        for reading.   The entire file is then read and displayed on
        the   monitor.    It  should  pose  no  problems   to   your
        understanding so no additional comments will be made.

             Compile  and  run  this program.   When it  requests  a
        filename,  enter  the  name and extension of any  text  file
        available, even one of the example C programs.

                              HOW DO WE PRINT?
                                        /* Chapter 10 - Program 8 */
#include "stdio.h"

main()
{
FILE *funny,*printer;
char c;

   funny = fopen("TENLINES.TXT","r"); /* open input file     */
   printer = fopen("PRN","w");        /* open printer file   */

   do {
      c = getc(funny);    /* get one character from the file */
      if (c != EOF) {
         putchar(c);      /* display it on the monitor       */
         putc(c,printer); /* print the character             */
      }
   } while (c != EOF);    /* repeat until EOF (end of file)  */

   fclose(funny);
   fclose(printer);
}

                              
             Load  the  last example file in this chapter,  the  one
        named  PRINTDAT.C  for an example of  how  to  print.   This
        program  should not present any surprises to you so we  will
        move very quickly through it.

             Once  again,  we  open TENLINES.TXT for reading and  we
        open PRN for writing.  Printing is identical to writing data
        to  a disk file except that we use a standard name  for  the
        filename.  Most C compilers use the reserved  "filename"  of
        "PRN" that instructs the compiler to send the output to  the
        printer.   There are other names that are used  occationally
        such  as "LPT", "LPT1", or "LPT2".  Check the  documentation
        for your particular compiler.

             Some  of  the newest compilers use  a  predefined  file
        pointer  such as "stdprn" for the print file.   Once  again,
        check your documentation.



                                   Page 75









                       Chapter 10 - File Input/Output


             The  program is simply a loop in which a  character  is
        read, and if it is not the EOF, it is displayed and printed.
        When the EOF is found, the input file and the printer output
        files are both closed.  Note that good programming  practice
        would include checking both file pointers to assure that the
        files were opened properly.

             You can now erase TENLINES.TXT from your disk.  We will
        not be using it in any of the later chapters.


        PROGRAMMING EXERCISES

        1.   Write a program  that will prompt for a filename for  a
             read file,  prompt for a filename for a write file, and
             open both plus a file to the printer. Enter a loop that
             will read a character,  and output it to the file,  the
             printer, and the monitor. Stop at EOF.

        2.   Prompt for a  filename to read. Read the file a line at
             a time and display it on the monitor with line numbers.

        3.   Modify ANYFILE.C to test if the file exists and print  a
             message  if it doesn't.  Use a method similar  to  that
             used in READCHAR.C.




























                                   Page 76

TOP

C tutorial Chapter 9 - Standard Input/Output
Chapter 9 - Standard Input/Output


                          THE STDIO.H HEADER FILE

                                       /* Chapter 9 - Program 1 */
#include "stdio.h"          /* standard header for input/output */

main()
{
char c;

   printf("Enter any characters, X = halt program.\n");

   do {
      c = getchar();    /* get a single character from the kb   */
      putchar(c);       /* display the character on the monitor */
   } while (c != 'X');  /* until an X is hit                    */

   printf("\nEnd of program.\n");
}
                          
                          
                          
             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 second 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 C compiler.  Each of the header files has  a
        specific  purpose and any or all of them can be included  in
        any program.

             Your C compiler uses the double quote marks to indicate
        that  the  search for the "include" file will begin  in  the
        current  directory,  and if it not found there,  the  search
        will  continue in the "include" directory as set up  in  the
        environment.   It  also uses the "less  than"  and  "greater
        than" signs to indicate that the file search should begin in
        the  directory  specified in the environment.  Most  of  the
        programs  in  this tutorial have the double  quotes  in  the
        "include" statements.  The next program uses the "<" and ">"
        to  illustrate the usage.  Note that this will result  is  a
        slightly  faster  (but  probably  unnoticeable)  compilation
        because  the  system will not bother to search  the  current
        directory.

                        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


                                  Page 59









                     Chapter 9 - Standard Input/Output


        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
        definition.   You will often,  when reading literature about
        C, find a reference to K & R.  This refers to the book, "The
        C  Programming Language", 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


                                  Page 60









                     Chapter 9 - Standard Input/Output


        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
        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  confident  that   you


                                  Page 61









                     Chapter 9 - Standard Input/Output


        understand  the above explanation,  we will go on to another
        program.

             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
                        
                                         /* Chapter 9 - Program 2 */
#include <stdio.h>

main()
{
char c;

   printf("Enter any characters, terminate program with X\n");

   do {
      c = getch();                     /* get a character */
      putchar(c);                  /* display the hit key */
   } while (c != 'X');

   printf("\nEnd of program.\n");
}
                        

             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 using the  "<"  and
        ">"  method of defining it. Then we define a variable  named
        "c",  and  finally 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 therefore 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,  however,  there  is  no  linefeed  with  the
        carriage return.

                          NOW WE NEED A LINE FEED

                                        /* Chapter 9 - Program 3 */
#include "stdio.h"
#define CR 13       /* this defines CR to be 13 */
#define LF 10       /* this defines LF to be 10 */

main()
{
char c;

   printf("Input any characters, hit X to stop.\n");

   do {
      c = getch();                    /* get a character */
      putchar(c);                     /* display the hit key */
      if (c == CR) putchar(LF);       /* if it is a carriage return
                                         put out a linefeed too */
   } while (c != 'X');

   printf("\nEnd of program.\n");
}
                          
                          
             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


                                  Page 62









                     Chapter 9 - Standard Input/Output


        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 left out the two #define statements and used
        "if  (c  ==  13)  putchar(10);" but it  would  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.

                           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
        "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


                                  Page 63









                     Chapter 9 - Standard Input/Output


        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

                                          /* Chapter 9 - Program 4 */
#include "stdio.h"

main()
{
int valin;

   printf("Input a number from 0 to 32767, stop with 100.\n");

   do {
      scanf("%d",&valin);   /* read a single integer value in */
      printf("The value is %d\n",valin);
   } while (valin != 100);

   printf("End of program\n");
}
                        
             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 a value to that variable in 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
        entering  the  value 100 with other values  following,  will
        cause   termination   before  the   following   values   are
        considered.


                                  Page 64









                     Chapter 9 - Standard Input/Output



                      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 stored in  something  other
        than 16 bits.  If that is the case, the same principles will
        be true but with 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

                           
                                          /* Chapter 9 - Program 5 */
#include "stdio.h"

main()
{
char big[25];

    printf("Input a character string, up to 25 characters.\n");
    printf("An X in column 1 causes the program to stop.\n");

    do {
       scanf("%s",big);
       printf("The string is -> %s\n",big);
    } while (big[0] != 'X');

    printf("End of program.\n");
}
                           
             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
        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


                                  Page 65









                     Chapter 9 - Standard Input/Output


        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.    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


                                                                         /* Chapter 9 - Program 6 */
main()
{
int numbers[5], result[5], index;
char line[80];

   numbers[0] = 74;
   numbers[1] = 18;
   numbers[2] = 33;
   numbers[3] = 30;
   numbers[4] = 97;

   sprintf(line,"%d %d      %d %d %d\n",numbers[0],numbers[1],
           numbers[2],numbers[3],numbers[4]);

   printf("%s",line);

   sscanf(line,"%d %d %d %d      %d",&result[4],&result[3],
           (result+2),(result+1),result);


   for (index = 0;index < 5;index++)
      printf("The final result is %d\n",result[index]);

}
                              
                              
             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.

             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


                                  Page 66









                     Chapter 9 - Standard Input/Output


        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 from an input device 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

                                        /* Chapter 9 - Program 7 */
#include "stdio.h"

main()
{
int index;

   for (index = 0;index < 6;index++) {
      printf("This line goes to the standard output.\n");
      fprintf(stderr,"This line goes to the error device.\n");
   }

   exit(4);  /* This can be tested with the DOS errorlevel
                command in a batch file. The number returned
                is used as follows;

                IF ERRORLEVEL 4 GOTO FOUR
                (continue here if less than 4)
                .
                .
                GOTO DONE
                :FOUR
                (continue here if 4 or greater)
                .
                .
                ONE
                                                                  */
}
                           
                           
             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.

             The  program  consists  of a  loop  with  two  messages
        output,  one  to the standard output device and the other to


                                  Page 67









                     Chapter 9 - Standard Input/Output


        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
        been  detected 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 compileing
        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 in case your
        documentation  does not include enough information to  allow
        you to use it.





                                  Page 68









                     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 69

TOP

C tutorial Chapter 8 -pointers
Chapter 8 - Pointers


                             WHAT IS A POINTER?

             Simply  stated,  a pointer is an address.   Instead  of
        being  a  variable,  it  is a pointer to a  variable  stored
        somewhere in the address space of the program.  It is always
        best to use an example so load the file named POINTER.C  and
        display  it on your monitor for an example of a program with
        some pointers in it.

             For  the moment,  ignore the data declaration statement
        where we define "index" and two other fields beginning  with
        a star.   It is properly called an asterisk, but for reasons
        we  will see later,  let's agree to call it a star.   If you
        observe  the  first statement,  it should be clear  that  we
        assign the value of 39 to the variable "index".   This is no
        surprise,  we  have been doing it for several programs  now.
        The  next  statement  however,  says to assign  to  "pt1"  a
        strange looking value,  namely the variable "index" with  an
        ampersand in front of it.   In this example, pt1 and pt2 are
        pointers,  and  the  variable "index" is a simple  variable.
        Now we have a problem.  We need to learn how to use pointers
        in a program, but to do so requires that first we define the
        means of using the pointers in the program.

             The  following two rules will be somewhat confusing  to
        you at first but we need to state the definitions before  we
        can  use  them.   Take your time,  and the whole thing  will
        clear up very quickly.

                          TWO VERY IMPORTANT RULES

             The  following two rules are very important when  using
        pointers and must be thoroughly understood.

        1.  A variable name with an ampersand in front of it defines
            the  address of the variable and therefore points to the
            variable.  You can therefore read line seven as "pt1  is
            assigned the value of the address of index".

        2.  A  pointer  with a "star" in front of it refers  to  the
            value of the variable pointed to by the  pointer.   Line
            ten  of the program can be read as "The stored (starred)
            value  to which the pointer "pt1" points is assigned the
            value  13".   Now  you can see why it is  convenient  to
            think of the asterisk as a star,  it sort of sounds like
            the word store.

                                  MEMORY AIDS

            1. Think of & as an address.
            2. Think of * as a star referring to stored.


                                  Page 52









                            Chapter 8 - Pointers



             Assume for the moment that "pt1" and "pt2" are pointers
        (we will see how to define them shortly).  As pointers, they
        do not contain a variable value but an address of a variable
        and  can  be  used to point to a variable.  Line  7  of  the
        program  assigns the pointer "pt1" to point to the  variable
        we have already defined as "index" because we have  assigned
        the address of "index" to "pt1".  Since we have a pointer to
        "index",  we  can manipulate the value of "index"  by  using
        either the variable name itself, or the pointer.

             Line 10 modifies the value by using the pointer.  Since
        the  pointer  "pt1"  points to the  variable  "index",  then
        putting  a star in front of the pointer name refers  to  the
        memory location to which it is pointing.  Line 10  therefore
        assigns to "index" the value of 13. Anyplace in the  program
        where it is permissible to use the variable name "index", it
        is  also permissible to use the name "*pt1" since  they  are
        identical in meaning until the pointer is reassigned to some
        other variable.

                              ANOTHER POINTER

             Just  to add a little intrigue to the system,  we  have
        another  pointer  defined in this  program,  "pt2".    Since
        "pt2" has not been assigned a value prior to statement 8, it
        doesn't point to anything, it contains garbage.  Of  course,
        that is also true of any variable until a value is  assigned
        to it. Statement 8 assigns "pt2" the same address as  "pt1",
        so  that now "pt2" also points to the variable "index".   So
        to continue the definition from the last paragraph, anyplace
        in  the program where it is permissible to use the  variable
        "index",  it  is  also permissible to use  the  name  "*pt2"
        because  they   are  identical in  meaning.   This  fact  is
        illustrated  in  the  first "printf"  statement  since  this
        statement  uses  the  three means of  identifying  the  same
        variable to print out the same variable three times.

                         THERE IS ONLY ONE VARIABLE

             Note carefully that,  even though it appears that there
        are three variables, there is really only one variable.  The
        two  pointers  point  to  the  single  variable.    This  is
        illustrated in the next statement which assigns the value of
        13  to  the  variable "index",  because that  is  where  the
        pointer  "pt1"  is pointing.   The next  "printf"  statement
        causes  the  new value of 13 to be printed out three  times.
        Keep  in mind that there is really only one variable  to  be
        changed, not three.




                                  Page 53









                            Chapter 8 - Pointers


             This is admittedly a very difficult concept,  but since
        it  is  used  extensively  in all but  the  most  trivial  C
        programs,  it  is  well  worth your time to stay  with  this
        material until you understand it thoroughly.

                        HOW DO YOU DECLARE A POINTER?

             Now  to  keep a promise and tell you how to  declare  a
        pointer.   Refer  to the third line of the program  and  you
        will  see  our  old familiar way of  defining  the  variable
        "index",  followed  by  two more  definitions.   The  second
        definition  can  be read as "the storage location  to  which
        "pt1"  points  will be an int  type  variable".   Therefore,
        "pt1" is a pointer to an int type variable.  Likewise, "pt2"
        is another pointer to an int type variable.

             A  pointer  must  be defined to point to some  type  of
        variable.   Following a proper definition, it cannot be used
        to point to any other type of variable or it will result  in
        a  "type incompatibility" error.   In the same manner that a
        "float"  type of variable cannot be added to an  "int"  type
        variable,  a pointer to a "float" variable cannot be used to
        point to an integer variable.

             Compile and run this program and observe that there  is
        only  one  variable  and the single  statement  in  line  10
        changes the one variable which is displayed three times.

                      THE SECOND PROGRAM WITH POINTERS

             In these few pages so far on pointers,  we have covered
        a lot of territory, but it is important territory.  We still
        have  a  lot  of  material to cover so stay in  tune  as  we
        continue  this important aspect of C.   Load the  next  file
        named  POINTER2.C  and display it on your monitor so we  can
        continue our study.

             In  this program we have defined several variables  and
        two pointers.   The first pointer named "there" is a pointer
        to  a "char" type variable and the second named "pt"  points
        to an "int" type variable.  Notice also that we have defined
        two  array variables named "strg" and "list".   We will  use
        them  to show the correspondence between pointers and  array
        names.

                  A STRING VARIABLE IS ACTUALLY A POINTER

             In  the programming language C,  a string  variable  is
        defined to be simply a pointer to the beginning of a string.
        This  will  take  some explaining.   Refer  to  the  example
        program  on  your monitor.   You will notice that  first  we


                                  Page 54









                            Chapter 8 - Pointers


        assign a string constant to the string variable named "strg"
        so we will have some data to work with.  Next, we assign the
        value  of the first element to the variable "one",  a simple
        "char" variable.   Next,  since the string name is a pointer
        by  definition  of the C language,  we can assign  the  same
        value to "two" by using the star and the string  name.   The
        result  of  the two assignments are such that "one" now  has
        the same value as "two", and both contain the character "T",
        the  first character in the string.   Note that it would  be
        incorrect  to  write  the ninth line as  "two  =  *strg[0];"
        because the star takes the place of the square brackets.

             For all practical purposes,  "strg" is a  pointer.   It
        does, however, have one restriction that a true pointer does
        not  have.   It cannot be changed like a variable,  but must
        always contain the initial value and therefore always points
        to  its  string.   It  could  be thought  of  as  a  pointer
        constant,  and in some applications you may desire a pointer
        that cannot be corrupted in any way.   Even though it cannot
        be changed, it can be used to refer to other values than the
        one  it is defined to point to,  as we will see in the  next
        section of the program.

             Moving ahead to line 13, the variable "one" is assigned
        the  value of the ninth variable (since the indexing  starts
        at zero) and "two" is assigned the same value because we are
        allowed to index a pointer to get to values farther ahead in
        the string.  Both variables now contain the character "a".

            The C programming language takes care of indexing for us
        automatically  by  adjusting the indexing for  the  type  of
        variable  the  pointer is pointing to.   In this  case,  the
        index  of  8  is simply added to the  pointer  value  before
        looking up the desired result because a "char" type variable
        is  one byte long.   If we were using a pointer to an  "int"
        type variable,  the index would be doubled and added to  the
        pointer  before  looking up the value because an "int"  type
        variable  uses two bytes per value stored.   When we get  to
        the chapter on structures,  we will see that a variable  can
        have  many,   even  into  the  hundreds  or  thousands,   of
        bytes  per  variable,  but  the  indexing  will  be  handled
        automatically for us by the system.

             Since "there" is already a pointer, it can be  assigned
        the  address  of  the  eleventh element  of  "strg"  by  the
        statement  in line 17 of the program.  Remember  that  since
        "there"  is a true pointer, it can be assigned any value  as
        long as that value represents a "char" type of address.   It
        should  be clear that the pointers must be "typed" in  order
        to  allow  the  pointer arithmetic  described  in  the  last



                                  Page 55









                            Chapter 8 - Pointers


        paragraph to be done properly.  The third and fourth outputs
        will be the same, namely the letter "c".

                             POINTER ARITHMETIC

             Not  all  forms  of  arithmetic are  permissible  on  a
        pointer.   Only  those things that make  sense,  considering
        that a pointer is an address somewhere in the computer.   It
        would  make sense to add a constant to an  address,  thereby
        moving it ahead in memory that number of places.   Likewise,
        subtraction  is permissible,  moving it back some number  of
        locations.   Adding  two  pointers together would  not  make
        sense  because absolute memory addresses are  not  additive.
        Pointer multiplication is also not allowed, as that would be
        a  funny number.   If you think about what you are  actually
        doing,  it will make sense to you what is allowed,  and what
        is not.

                         NOW FOR AN INTEGER POINTER

             The  array named "list" is assigned a series of  values
        from  100  to 199 in order to have some data to  work  with.
        Next  we  assign the pointer "pt" the address  of  the  28th
        element  of the list and print out the same value both  ways
        to  illustrate that the system truly will adjust  the  index
        for the "int" type variable.  You should spend some time  in
        this program until you feel you fairly well understand these
        lessons on pointers.

             Compile and run POINTER2.C and study the output.

             You  may recall that back in the lesson on functions we
        mentioned that there were two ways to get variable data back
        from a function.   One way is through use of the array,  and
        you should be right on the verge of guessing the other  way.
        If your guess is through use of a pointer,  you are correct.
        Load  and display the program named TWOWAY.C for an  example
        of this.

                    FUNCTION DATA RETURN WITH A POINTER

             In  TWOWAY.C,  there  are two variables defined in  the
        main program "pecans" and "apples".   Notice that neither of
        these is defined as a pointer.   We assign values to both of
        these  and print them out,  then call the  function  "fixup"
        taking with us both of these values.   The variable "pecans"
        is  simply  sent  to the function,  but the address  of  the
        variable  "apples" is sent to the function.   Now we have  a
        problem.   The two arguments are not the same, the second is
        a pointer to a variable.  We must somehow alert the function
        to  the  fact  that it is supposed  to  receive  an  integer


                                  Page 56









                            Chapter 8 - Pointers


        variable  and a pointer to an integer variable.   This turns
        out   to  be  very  simple.    Notice  that  the   parameter
        definitions in the function define "nuts" as an integer, and
        "fruit"  as a pointer to an integer.   The call in the  main
        program  therefore  is now in agreement  with  the  function
        heading and the program interface will work just fine.

             In  the body of the function,  we print the two  values
        sent  to  the function,  then modify them and print the  new
        values out.   This should be perfectly clear to you by  now.
        The  surprise occurs when we return to the main program  and
        print out the two values again.  We will find that the value
        of  pecans will be restored to its value before the function
        call  because  the C language makes a copy of  the  item  in
        question and takes the copy to the called function,  leaving
        the original intact.   In the case of the variable "apples",
        we  made  a copy of a pointer to the variable and  took  the
        copy of the pointer to the function.  Since we had a pointer
        to  the  original variable,  even though the pointer  was  a
        copy,  we  had  access  to the original variable  and  could
        change  it in the function.   When we returned to  the  main
        program,  we  found  a  changed value in  "apples"  when  we
        printed it out.

             By  using  a pointer in a function call,  we  can  have
        access  to the data in the function and change it in such  a
        way  that when we return to the calling program,  we have  a
        changed  value  of data.    It must be pointed out  however,
        that  if you modify the value of the pointer itself  in  the
        function,  you  will have a restored pointer when you return
        because the pointer you use in the function is a copy of the
        original.  In this example, there was no pointer in the main
        program because we simply sent the address to the  function,
        but  in  many  programs you will use  pointers  in  function
        calls.  One of the places you will find need for pointers in
        function  calls  will be when you request data  input  using
        standard  input/output routines.   These will be covered  in
        the next two chapters.

             Compile and run TWOWAY.C and observe the output.

                           POINTERS ARE VALUABLE

             Even  though  you are probably somewhat intimidated  at
        this point by the use of pointers,  you will find that after
        you  gain experience,  you will use them profusely  in  many
        ways.  You will also use pointers in every program you write
        other than the most trivial because they are so useful.  You
        should  probably  go  over this material  carefully  several
        times until you feel comfortable with it because it is  very
        important  in the area of input/output which is next on  the
        agenda.


source codes

POINTER.C:
========
                                         /* Chapter 8 - Program 1 */
main()                             /* illustration of pointer use */
{
int index,*pt1,*pt2;

   index = 39;                             /* any numerical value */
   pt1 = &index;                          /* the address of index */
   pt2 = pt1;
   printf("The value is %d %d %d\n",index,*pt1,*pt2);
   *pt1 = 13;                  /* this changes the value of index */
   printf("The value is %d %d %d\n",index,*pt1,*pt2);
}


POINTER2.C
========
                                         /* Chapter 8 - Program 2 */
main()
{
char strg[40],*there,one,two;
int *pt,list[100],index;

   strcpy(strg,"This is a character string.");

   one = strg[0];                    /* one and two are identical */
   two = *strg;
   printf("The first output is %c %c\n",one,two);

   one = strg[8];                   /* one and two are indentical */
   two = *(strg+8);
   printf("the second output is %c %c\n",one,two);

   there = strg+10;           /* strg+10 is identical to strg[10] */
   printf("The third output is %c\n",strg[10]);
   printf("The fourth output is %c\n",*there);

   for (index = 0;index < 100;index++)
      list[index] = index + 100;
   pt = list + 27;
   printf("The fifth output is %d\n",list[27]);
   printf("The sixth output is %d\n",*pt);
}

TWOWAY.C
========

                                        /* Chapter 8 - Program 3 */
main()
{
int pecans,apples;

   pecans = 100;
   apples = 101;
   printf("The starting values are %d %d\n",pecans,apples);

                           /* when we call "fixup"          */
   fixup(pecans,&apples);  /* we take the value of pecans   */
                           /* we take the address of apples */

   printf("The ending values are %d %d\n",pecans,apples);
}

fixup(nuts,fruit)             /* nuts is an integer value   */
int nuts,*fruit;              /* fruit points to an integer */
{
   printf("The values are %d %d\n",nuts,*fruit);
   nuts = 135;
   *fruit = 172;
   printf("The values are %d %d\n",nuts,*fruit);
}

TOP

C tutorial (chapter 7)
Chapter 7 - Strings and Arrays


                             WHAT IS A STRING?

             A  string is a group of characters,  usually letters of
        the  alphabet.   In order to format your printout in such  a
        way that it looks nice, has meaningful titles and names, and
        is  esthetically  pleasing to you and the people  using  the
        output of your program,  you need the ability to output text
        data.  Actually you have already been using strings, because
        the second program in this tutorial,  way back in Chapter 2,
        output a message that was handled internally as a string.  A
        complete  definition  is  a  series  of  "char"  type   data
        terminated by a NULL character, which is a zero.

             When  C  is going to use a string of data in some  way,
        either  to compare it with another,  output it,  copy it  to
        another string,  or whatever, the functions are set up to do
        what they are called to do until a NULL, which is a zero, is
        detected.

                             WHAT IS AN ARRAY?

             An array is a series of homogeneous pieces of data that
        are all identical in type, but the type can be quite complex
        as  we will see when we get to the chapter of this  tutorial
        discussing structures.  A string is simply a special case of
        an array, a series of char type data.


                                         /* Chapter 7 - Program 1 */
main()
{
char name[5];       /* define a string of characters */

   name[0] = 'D';
   name[1] = 'a';
   name[2] = 'v';
   name[3] = 'e';
   name[4] = 0;     /* Null character - end of text */

   printf("The name is %s\n",name);
   printf("One letter is %c\n",name[2]);
   printf("Part of the name is %s\n",&name[1]);
}


             The  best way to see these principles is by use  of  an
        example,  so  load  the program CHRSTRG.C and display it  on
        your monitor.   The first thing new is the line that defines
        a "char" type of data entity.  The square brackets define an
        array subscript in C, and in the case of the data definition
        statement,  the  5 in the brackets defines 5 data fields  of
        type  "char" all defined as the variable "name".   In the  C
        language,  all subscripts start at 0 and increase by 1  each
        step  up  to  the  maximum which in  this  case  is  4.   We
        therefore  have  5 "char" type variables  named,  "name[0]",
        "name[1]",  "name[2]",  "name[3]",  and "name[4]".  You must
        keep in mind that in C, the subscripts actually go from 0 to
        one   less  than  the  number  defined  in  the   definition
        statement.  This is due to the original definition of C  and
        these   limits  cannot  be  changed  or  redefined  by   the
        programmer.

                         HOW DO WE USE THE STRING?

             The  variable  "name" is therefore a string  which  can
        hold up to 5 characters, but since we need room for the NULL
        terminating  character, there are actually only four  useful
        characters.   To load something useful into the  string,  we
        have  5 statements, each of which assigns  one  alphabetical


                                  Page 45









                      Chapter 7 - Strings and Arrays


        character  to  one of the string characters.   Finally,  the
        last place in the string is filled with the numeral 0 as the
        end indicator and the string is complete.  (A "define" would
        allow us to use "NULL" instead of a zero, and this would add
        greatly  to  the clarity of the program. It  would  be  very
        obvious that this was a NULL and not simply a zero for  some
        other purpose.) Now that we have the string, we will  simply
        print  it  out  with some other string data  in  the  output
        statement.

             The %s is the output definition to output a string  and
        the  system  will output characters starting with the  first
        one in "name" until it comes to the NULL character,  and  it
        will quit.   Notice that in the "printf" statement, only the
        variable  name "name" needs to be given,  with no  subscript
        since  we  are  interested  in starting  at  the  beginning.
        (There  is  actually another reason that only  the  variable
        name  is  given without brackets.   The discussion  of  that
        topic will be given in the next chapter.)

                        OUTPUTTING PART OF A STRING

             The  next "printf" illustrates that we can  output  any
        single  character of the string by using the "%c" and naming
        the particular character of "name" we want by including  the
        subscript.   The last "printf" illustrates how we can output
        part  of the string by stating the starting point by using a
        subscript.   The & specifies the address of  "name[1]".   We
        will  study this in the next chapter but I thought you would
        benefit from a little glimpse ahead.

             This example may make you feel that strings are  rather
        cumbersome  to  use since you have to set up each  character
        one  at  a time.   That is an incorrect  conclusion  because
        strings  are  very easy to use as we will see  in  the  next
        example program.

             Compile and run this program.


                                         /* Chapter 7 - Program 2 */
main()
{
char name1[12],name2[12],mixed[25];
char title[20];

   strcpy(name1,"Rosalinda");
   strcpy(name2,"Zeke");
   strcpy(title,"This is the title.");

   printf("     %s\n\n",title);
   printf("Name 1 is %s\n",name1);
   printf("Name 2 is %s\n",name2);

   if(strcmp(name1,name2)>0)  /* returns 1 if name1 > name2 */
      strcpy(mixed,name1);
   else
      strcpy(mixed,name2);

   printf("The biggest name alpabetically is %s\n",mixed);

   strcpy(mixed,name1);
   strcat(mixed,"  ");
   strcat(mixed,name2);
   printf("Both names are %s\n",mixed);
}


                          SOME STRING SUBROUTINES

             Load  the  example program STRINGS.C for an example  of
        some  ways  to use strings.  First we define  four  strings.
        Next  we  come  to a new function that you  will  find  very
        useful,  the "strcpy" function,  or string copy.   It copies
        from  one  string  to another until it  comes  to  the  NULL
        character.  Remember that the NULL is actually a "0" and  is
        added to the character string by the system.  It is easy  to
        remember which one gets copied to which if you think of them
        like an assignment statement.  Thus if you were to say,  for
        example, "x = 23;", the data is copied from the right entity


                                  Page 46









                      Chapter 7 - Strings and Arrays


        to the left one.  In the "strcpy" function, the data is also
        copied  from  the right entity to the left,  so  that  after
        execution  of  the first statement, name1 will  contain  the
        string "Rosalinda", but without the double quotes, they  are
        the  compiler's  way  of knowing that  you  are  defining  a
        string.

             Likewise,  "Zeke"  is copied into "name2" by the second
        statement,  then the "title" is copied.   The title and both
        names are then printed out.   Note that it is not  necessary
        for  the  defined string to be exactly the same size as  the
        string it will be called upon to store,  only that it is  at
        least  as long as the string plus one more character for the
        NULL.

                      ALPHABETICAL SORTING OF STRINGS

             The  next function we will look at is the  "strcmp"  or
        the  string  compare function.   It will return a 1  if  the
        first string is larger than the second, zero if they are the
        same  length  and have the same characters,  and -1  if  the
        first  string  is  smaller  than the  second.   One  of  the
        strings,  depending  on the result of the compare is  copied
        into   the   variable  "mixed",   and   the   largest   name
        alphabetically  is  printed  out.   It  should  come  as  no
        surprise   to   you   that  "Zeke"  wins   because   it   is
        alphabetically  larger,  length  doesn't  matter,  only  the
        alphabet.  It might be wise to mention that the result would
        also depend on whether the letters were upper or lower case.
        There are functions available with your C compiler to change
        the  case of a string to all upper or all lower case if  you
        desire.   These  will be used in an example program later in
        this tutorial.

                             COMBINING STRINGS

             The last four statements have another new feature,  the
        "strcat",  or string concatenation function.   This function
        simply  adds the characters from one string onto the end  of
        another string taking care to adjust the NULL so  everything
        is  still all right.   In this case,  "name1" is copied into
        "mixed",  then two blanks are concatenated to  "mixed",  and
        finally  "name2"  is concatenated to the  combination.   The
        result  is printed out with both names in the  one  variable
        "mixed".

             Strings  are  not difficult and are  extremely  useful.
        You should spend some time getting familiar with them before
        proceeding on to the next topic.




                                  Page 47









                      Chapter 7 - Strings and Arrays


             Compile  and run this program and observe  the  results
        for compliance with this definition.

                            AN ARRAY OF INTEGERS

                                          /* Chapter 7 - Program 3 */
main()
{
int values[12];
int index;

   for (index = 0;index < 12;index++)
      values[index] = 2 * (index + 4);

   for (index = 0;index < 12;index++)
      printf("The value at index = %2d is %3d\n",index,values[index]);

}


             Load the file INTARRAY.C and display it on your monitor
        for  an  example of an array of integers.   Notice that  the
        array is defined in much the same way we defined an array of
        char  in  order to do the string manipulations in  the  last
        section.   We  have  12 integer variables to work  with  not
        counting the one named "index".   The names of the variables
        are "values[0]",  "values[1]", ... , and "values[11]".  Next
        we have a loop to assign nonsense, but well defined, data to
        each  of  the  12 variables, then print all  12  out.   Note
        carefully that each element of the array is simply an  "int"
        type variable capable of storing an integer value.  The only
        difference  between the variables "index"  and  "values[2]",
        for  example,  is in the way you address them.   You  should
        have  no  trouble following this program, but  be  sure  you
        understand  it.  Compile and run it to see if it  does  what
        you expect it to do.

                      AN ARRAY OF FLOATING POINT DATA

                                         /* Chapter 7 - Program 4 */
char name1[] = "First Program Title";

main()
{
int index;
int stuff[12];
float weird[12];
static char name2[] = "Second Program Title";

   for (index = 0;index < 12;index++) {
      stuff[index] = index + 10;
      weird[index] = 12.0 * (index + 7);
   }

   printf("%s\n",name1);
   printf("%s\n\n",name2);
   for (index = 0;index < 12;index++)
      printf("%5d %5d %10.3f\n",index,stuff[index],weird[index]);
}


             Load  and display the program named BIGARRAY.C  for  an
        example  of  a program with an array of "float"  type  data.
        This program has an extra feature to illustrate how  strings
        can  be  initialized.   The  second  line  of  the   program
        illustrates to you how to initialize a string of characters.
        Notice  that the square brackets are empty leaving it up  to
        the  compiler  to count the characters and  allocate  enough
        space  for  our  string  including  the  terminating   NULL.
        Another string is initialized in the body of the program but
        it  must be declared "static" here.  This prevents  it  from
        being allocated as an "automatic" variable and allows it  to
        retain  the  string once the program is started.   There  is
        nothing  else new here, the variables are assigned  nonsense
        data  and  the results of all the nonsense are  printed  out
        along  with a header.  This program should also be easy  for
        you to follow, so study it until you are sure of what it  is
        doing before going on to the next topic.

                     GETTING DATA BACK FROM A FUNCTION

                                          /* Chapter 7 - Program 5 */
main()
{
int index;
int matrix[20];

   for (index = 0;index < 20;index++)              /* generate data */
      matrix[index] = index + 1;

   for (index = 0;index < 5;index++)         /* print original data */
      printf("Start  matrix[%d] = %d\n",index,matrix[index]);

   dosome(matrix);              /* go to a function & modify matrix */

   for (index = 0;index < 5;index++)       /* print modified matrix */
      printf("Back   matrix[%d] = %d\n",index,matrix[index]);
}

dosome(list)                 /* This will illustrate returning data */
int list[];
{
int i;

   for (i = 0;i < 5;i++)                   /* print original matrix */
      printf("Before matrix[%d] = %d\n",i,list);

   for (i = 0;i < 20;i++)                   /* add 10 to all values */
      list += 10;

   for (i = 0;i < 5;i++)                   /* print modified matrix */
      printf("After  matrix[%d] = %d\n",i,list);
}


             Back  in chapter 5 when we studied functions,  I hinted
        to you that there was a way to get data back from a function
        by  using  an array,  and that is true.   Load  the  program
        PASSBACK.C for an example of doing that.   In this  program,
        we  define  an array of 20 variables  named  "matrix",  then
        assign  some nonsense data to the variables,  and print  out
        the  first five.   Then we call the function "dosome" taking


                                  Page 48









                      Chapter 7 - Strings and Arrays


        along  the entire array by putting the name of the array  in
        the parentheses.

             The  function  "dosome" has a name in  its  parentheses
        also but it prefers to call the array "list".   The function
        needs  to be told that it is really getting an array  passed
        to  it  and that the array is of type "int".  Line  20  does
        that  by  defining "list" as an integer  type  variable  and
        including  the square brackets to indicate an array.  It  is
        not necessary to tell the function how many elements are  in
        the  array,  but you could if you so desired.   Generally  a
        function  works with an array until some end-of-data  marker
        is  found,  such  as  a NULL for a  string,  or  some  other
        previously  defined  data or pattern.  Many  times,  another
        piece of data is passed to the function with a count of  how
        many elements to work with.  In our present illustration, we
        will use a fixed number of elements to keep it simple.

             So far nothing is different from the previous functions
        we  have called except that we have passed more data  points
        to  the function this time than we ever have before,  having
        passed 20 integer values.  We print out the first 5 again to
        see if they did indeed get passed here.   Then we add ten to
        each of the elements and print out the new values.   Finally
        we return to the main program and print out the same 5  data
        points.   We  find that we have indeed modified the data  in
        the function,  and when we returned to the main program,  we
        brought  the changes back.   Compile and run this program to
        verify this conclusion.

                         ARRAYS PASS DATA BOTH WAYS

             We  stated during our study of functions that  when  we
        passed data to a function,  the system made a copy to use in
        the  function which was thrown away when we returned.   This
        is not the case with arrays.   The actual array is passed to
        the  function  and  the function can modify it  any  way  it
        wishes  to.    The  result  of  the  modifications  will  be
        available  back  in  the calling  program.   This  may  seem
        strange  to  you that arrays are  handled  differently  from
        single point data, but they are.  It really does make sense,
        but  you  will  have  to wait until we get  to  pointers  to
        understand it.

                         A HINT AT A FUTURE LESSON

             Another way of getting data back from a function to the
        calling program is by using pointers which we will cover  in
        the  next chapter.   When we get there we will find that  an
        array  is in reality a pointer to a list of  values.   Don't
        let  that  worry  you now,  it will make sense when  we  get


                                  Page 49









                      Chapter 7 - Strings and Arrays


        there.  In the meantime concentrate on arrays and understand
        the  basics  of  them because when we get to  the  study  of
        structures  we will be able to define some pretty  elaborate
        arrays.


                        MULTIPLY DIMENSIONED ARRAYS

                                           /* Chapter 7 - Program 6 */
main()
{
int i,j;
int big[8][8],large[25][12];

   for (i = 0;i < 8;i++)
      for (j = 0;j < 8;j++)
         big[j] = i * j;       /* This is a multiplication table */

   for (i = 0;i < 25;i++)
      for (j = 0;j < 12;j++)
         large[j] = i + j;           /* This is an addition table */

   big[2][6] = large[24][10]*22;
   big[2][2] = 5;
   big[big[2][2]][big[2][2]] = 177;     /* this is big[5][5] = 177; */

   for (i = 0;i < 8;i++) {
      for (j = 0;j < 8;j++)
         printf("%5d ",big[j]);
      printf("\n");               /* newline for each increase in i */
   }
}


             Load  and  display  the file named  MULTIARY.C  for  an
        example  of a program with doubly dimensioned  arrays.   The
        variable "big" is an 8 by 8 array that contains 8 times 8 or
        64  elements total.  The first element is  "big[0][0]",  and
        the  last  is "big[7][7]".  Another array named  "large"  is
        also  defined  which is not square to  illustrate  that  the
        array need not be square.  Both are filled up with data, one
        representing  a  multiplication table and  the  other  being
        formed into an addition table.

             To illustrate that individual elements can be  modified
        at will,  one of the elements of "big" is assigned the value
        from  one of the elements of "large" after being  multiplied
        by 22.  Next "big[2][2]" is assigned the arbitrary value  of
        5,  and  this value is used for the subscripts of  the  next
        assignment statement.  The third assignment statement is  in
        reality  "big[5][5]  = 177" because each of  the  subscripts
        contain  the value 5.  This is only done to illustrate  that
        any  valid expression can be used for a subscript.  It  must
        only meet two conditions, it must be an integer (although  a
        "char"  will work just as well), and it must be  within  the
        range of the subscript it is being used for.

             The  entire matrix variable "big" is printed out  in  a
        square  form so you can check the values to see if they  did
        get set the way you expected them to.



















                                  Page 50









                      Chapter 7 - Strings and Arrays


        PROGRAMMING EXERCISES

        1.   Write  a  program with  three short  strings,  about  6
             characters each, and use "strcpy" to copy "one", "two",
             and  "three" into them.  Concatenate the three  strings
             into one string and print the result out 10 times.

        2.   Define  two  integer  arrays,  each 10  elements  long,
             called "array1" and "array2".  Using a loop,  put some
             kind  of  nonsense data in each and add them  term  for
             term  into  another 10 element  array  named  "arrays".
             Finally,  print  all  results in a table with an  index
             number.
             1     2 +  10 =  12
             2     4 +  20 =  24
             3     6 +  30 =  36   etc.

             Hint; The print statement will be similar to;
                printf("%4d %4d + %4d = %4d\n",index,array1[index],
                        array2[index],arrays[index]);








                                  Page 51

TOP

C tutorial (Chapter 6)
Chapter 6 - Defines and Macros


              DEFINES AND MACROS ARE AIDS TO CLEAR PROGRAMMING

******* DEFINE.C

#define START  0  /* Starting point of loop */
#define ENDING 9  /* Ending point of loop */
#define MAX(A,B)  ((A)>(B)?(A)B))  /* Max macro definition */
#define MIN(A,B)  ((A)>(B)?(B)A))  /* Min macro definition */

main()
{
int index,mn,mx;
int count = 5;

   for (index = START;index <= ENDING;index++) {
      mx = MAX(index,count);
      mn = MIN(index,count);
      printf("Max is %d and min is %d\n",mx,mn);
   }
}

**********

             Load and display the file named DEFINE.C for your first
        look  at some defines and macros.  Notice lines 2 through  5
        of the program, each starting with the word "#define".  This
        is  the way all defines and macros are defined.  Before  the
        actual  compilation  starts,  the compiler  goes  through  a
        preprocessor  pass  to resolve all of the defines.   In  the
        present case, it will find every place in the program  where
        the combination "START" is found and it will simply  replace
        it  with the 0 since that is the definition.   The  compiler
        itself  will  never see the word "START", so as far  as  the
        compiler  is concerned, the zeros were always  there.   Note
        that  if  the string is found in a string constant or  in  a
        comment, it will not be changed.

            It  should be clear to you by now that putting the  word
        "START"  in your program instead of the numeral 0 is only  a
        convenience  to you and actually acts like a  comment  since
        the  word "START" helps you to understand what the  zero  is
        used for.

             In  the  case  of a very small program,  such  as  that
        before  you,  it doesn't really matter what  you  use.   If,
        however,  you  had a 2000 line program before  you  with  27
        references  to "START", it would be a  completely  different
        matter.  If you wanted to change all of the "START"s in  the
        program  to a new number, it would be simple to  change  the
        one  #define,  but difficult to find and change all  of  the
        references  to it manually, and possibly disastrous  if  you
        missed one or two of the references.

             In  the  same manner,  the preprocessor will  find  all
        occurrences of the word "ENDING" and change them to 9,  then
        the  compiler  will  operate  on the changed  file  with  no
        knowledge that "ENDING" ever existed.

             It is a fairly common practice in C programming to  use
        all  capital letters for a symbolic constant such as "START"
        and  "ENDING"  and use all lower case letters  for  variable
        names.  You can use any method you choose since it is mostly
        a matter of personal taste.

                           IS THIS REALLY USEFUL?

             When  we  get  to the  chapters  discussing  input  and
        output,  we  will need an indicator to tell us when we reach
        the end-of-file of an input file.  Since different compilers
        use  different numerical values for this, although most  use
        either a zero or a minus 1, we will write the program with a
        "define" to define the EOF used by our particular  compiler.


                                  Page 41









                       Chapter 6 - Defines and Macros


        If at some later date, we change to a new compiler, it is  a
        simple matter to change this one "define" to fix the  entire
        program.   In  most C compilers, the EOF is defined  in  the
        STDIO.H file.  You can observe this for yourself by  listing
        this file.

                              WHAT IS A MACRO?

             A macro is nothing more than another define,  but since
        it  is capable of at least appearing to perform some logical
        decisions  or  some math functions,  it has a  unique  name.
        Consider line 4 of the program on your screen for an example
        of  a macro.  In this case, anytime the  preprocessor  finds
        the  word  "MAX"  followed by a  group  in  parentheses,  it
        expects  to find two terms in the parentheses and will do  a
        replacement  of the terms into the second definition.   Thus
        the  first  term  will  replace  every  "A"  in  the  second
        definition and the second term will replace every "B" in the
        second definition.  When line 13 of the program is  reached,
        "index" will be substituted for every "A", and "count"  will
        be substituted for every "B".  Once again, it must be stated
        that  string  constants and comments will not  be  affected.
        Remembering  the  cryptic construct we studied a  couple  of
        chapters ago will reveal that "mx" will receive the  maximum
        value  of  "index" or "count".  In like  manner,  the  "MIN"
        macro  will  result in "mn" receiving the minimum  value  of
        "index" or "count".

             The  results are then printed out.  There are a lot  of
        seemingly extra parentheses in the macro definition but they
        are  not  extra, they are essential.  We  will  discuss  the
        extra parentheses in our next program.

             Compile and run DEFINE.C.

                         LETS LOOK AT A WRONG MACRO


                           /* MACRO.C Chapter 6 - Program 2 */
#define WRONG(A) A*A*A          /* Wrong macro for cube     */
#define CUBE(A) (A)*(A)*(A)     /* Right macro for cube     */
#define SQUR(A) (A)*(A)         /* Right macro for square   */
#define ADD_WRONG(A) (A)+(A)    /* Wrong macro for addition */
#define ADD_RIGHT(A) ((A)+(A))  /* Right macro for addition */
#define START 1
#define STOP  7

main()
{
int i,offset;

   offset = 5;
   for (i = START;i <= STOP;i++) {
      printf("The square of %3d is %4d, and its cube is %6d\n",
              i+offset,SQUR(i+offset),CUBE(i+offset));
      printf("The wrong of  %3d is %6d\n",i+offset,WRONG(i+offset));
   }

   printf("\nNow try the addition macro's\n");
   for (i = START;i <= STOP;i++) {
      printf("Wrong addition macro = %6d, and right = %6d\n"
                               ,5*ADD_WRONG(i),5*ADD_RIGHT(i));
   }
}


              Load  the  file named MACRO.C and display  it  on  your
        screen for a better look at a macro and its use.  The second
        line  defines a macro named "WRONG" that appears to get  the
        cube of "A",  and indeed it does in some cases, but it fails
        miserably in others.  The second macro named "CUBE" actually
        does get the cube in all cases.

             Consider  the program itself where the CUBE of i+offset
        is  calculated.   If  i is 1,  which it is  the  first  time
        through,  then  we will be looking for the cube of 1+5 =  6,
        which  will result in 216.  When using "CUBE",  we group the
        values like this, (1+5)*(1+5)*(1+5) = 6*6*6 = 216.  However,
        when we use WRONG,  we group them as 1+5*1+5*1+5 = 1+5+5+5 =
        16 which is a wrong answer.   The parentheses are  therefore


                                  Page 42









                       Chapter 6 - Defines and Macros


        required  to  properly  group the  variables  together.   It
        should  be clear to you that either "CUBE" or "WRONG"  would
        arrive  at  a correct answer for a single  term  replacement
        such as we did in the last program.   The correct values  of
        the  cube  and the square of the numbers are printed out  as
        well as the wrong values for your inspection.

             In line 5 we define "ADD_WRONG" according to the  above
        rules  but  we still have a problem when we try to  use  the
        macro in line 23 and 24.  In line 24 when we say we want the
        program  to calculate 5*ADD_WRONG(i) with i = 1, we get  the
        result  5*1 + 1 which evaluates to 5 + 1 or 6, and  this  is
        most  assuredly not what we had in mind.  We  really  wanted
        the result to be 5*(1 + 1) = 5*2 = 10 which is the answer we
        get  when we use the macro named "ADD_RIGHT(i)",  because of
        the extra parentheses in the definition given in line 6.   A
        lttle time spent studying the program and the result will be
        worth your effort in understanding how to use macro's.

             In   order   to  prevent  the  above   problems,   most
        experienced  C programmers include parentheses  around  each
        variable  in a macro and additional parentheses  around  the
        entire expression.

             The remainder of the program is simple and will be left
        to your inspection and understanding.

                      WHAT IS AN ENUMERATION VARIABLE?

                                         /* ENUM.C Chapter 6 - Program 3 */
main()
{
enum {win,tie,bye,lose,no_show} result;
enum {sun,mon,tues,wed,thur,fri,sat} days;

   result = win;
   printf("    win = %d\n",result);
   result = lose;
   printf("   lose = %d\n",result);
   result = tie;
   printf("    tie = %d\n",result);
   result = bye;
   printf("    bye = %d\n",result);
   result = no_show;
   printf("no show = %d\n\n",result);

   for(days = mon;days < fri;days++)
      printf("The day code is %d\n",days);
}


             Load  and  display  the program  named  ENUM.C  for  an
        example  of  how to use the "enum" type  variable.   Line  4
        contains the first "enum" type variable named "result" which
        is a variable which can take on any of the values  contained
        within  the parentheses.  Actually the variable "result"  is
        an "int" type variable but can be assigned any of the values
        defined for it.  The names within the parentheses are  "int"
        type  constants and can be used anywhere it is legal to  use
        an "int" type constant.  The constant "win" is assigned  the
        vallue of 0, "tie" the value 1, "bye" the value 2, etc.

             In  use,  the variable "result" is used just  like  any
        "int"  variable would be used as can be seen by its  use  in
        the program.  The "enum" type of variable is intended to  be
        used  by you, the programmer, as a coding aid since you  can
        use  a  constant named "mon" for control  structures  rather
        than  the meaningless (at least to you) value of 1.   Notice
        that  "days" is assigned the values of days of the  week  in
        the remainder of the program.  If you were to use a "switch"
        statement,  it  would  be much more meaningful  to  use  the
        labels "sun", "mon", etc, rather than the more awkward 0, 1,
        2, etc.


                                  Page 43









                       Chapter 6 - Defines and Macros




        PROGRAMMING EXERCISES

        1.   Write a program to count from 7 to -5 by counting down.
             Use #define statements to define the limits. (Hint, you
             will  need to use a decrementing variable in the  third
             part of the "for" loop control.

        2.   Add some printf statements in MACRO.C to see the result
             of the erroneous and correct addition macros.

TOP

C tutorial [chapter 5]
Chapter 5 - Functions, variables, and prototypes


                      OUR FIRST USER DEFINED FUNCTION

                                          /* Chapter 5 - Program 1 */
int sum; /* This is a global variable */

main()
{
int index;

   header();        /* This calls the function named header */
   for (index = 1;index <= 7;index++)
     square(index); /* This calls the square function */
   ending();        /* This calls the ending function */
}

header()        /* This is the function named header */
{
   sum = 0;     /* Initialize the variable "sum" */
   printf("This is the header for the square program\n\n");
}

square(number)   /* This is the square function */
int number;
{
int numsq;

   numsq = number * number;  /* This produces the square */
   sum += numsq;
   printf("The square of %d is %d\n",number,numsq);
}

ending()   /* This is the ending function */
{
   printf("\nThe sum of the squares is %d\n",sum);
}


             Load  and examine the file SUMSQRES.C (above) for an example of
        a C program with functions.   Actually this is not the first
        function  we have encountered because the "main" program  we
        have been using all along is technically a function,  as  is
        the  "printf" function.   The "printf" function is a library
        function that was supplied with your compiler.

             Notice the executable part of this program which begins
        in  line  8.   It  begins  with  a  line  that  simply  says
        "header()",  which  is the way to call  any  function.   The
        parentheses are required because the C compiler uses them to
        determine  that  it  is a function call  and  not  simply  a
        misplaced variable.  When the program comes to this line  of
        code, the function named "header" is called, its  statements
        are executed, and control returns to the statement following
        this call.  Continuing on we come to a "for" loop which will
        be  executed 7 times and which calls another function  named
        "square" each time through the loop, and finally a  function
        named "ending" will be called and executed.  For the  moment
        ignore  the  "index"  in  the parentheses  of  the  call  to
        "square".  We have seen that this program therefore calls  a
        header, 7 square calls, and an ending. Now we need to define
        the functions.

                           DEFINING THE FUNCTIONS

             Following the main program you will see another program
        that  follows all of the rules set forth so far for a "main"
        program  except that it is named "header()".   This  is  the
        function which is called from within the main program.  Each
        of  these  statements are executed,  and when they  are  all
        complete, control returns to the main program.

             The  first  statement sets the variable "sum" equal  to
        zero because we will use it to accumulate a sum of  squares.
        Since  the  variable  "sum" is defined as  an  integer  type
        variable  prior to the main program,  it is available to  be
        used  in  any of the following functions.   It is  called  a
        "global" variable,  and it's scope is the entire program and
        all  functions.   More  will  be  said about  the  scope  of
        variables near the end of this chapter.  The next  statement
        outputs  a header message to the monitor.   Program  control
        then  returns  to  the  main  program  since  there  are  no
        additional statements to execute in this function.

             It should be clear to you that the two executable lines
        from  this  function  could be moved to  the  main  program,
        replacing the header call,  and the program would do exactly
        the same thing that it does as it is now written.  This does


                                  Page 29









            Chapter 5 - Functions, variables, and prototypes


        not minimize the value of functions,  it merely  illustrates
        the operation of this simple function in a simple way.   You
        will find functions to be very valuable in C programming.

                       PASSING A VALUE TO A FUNCTION

             Going  back  to the main program,  and the  "for"  loop
        specifically,  we find the new construct from the end of the
        last lesson used in the last part of the "for" loop,  namely
        the  "index++".  You should get used to seeing this, as  you
        will see it a lot in C programs.

             In the call to the function "square",  we have an added
        feature, namely the variable "index" within the parentheses.
        This  is  an indication to the compiler that when you go  to
        the function,  you wish to take along the value of index  to
        use in the execution of that function.  Looking ahead at the
        function  "square",  we  find that another variable name  is
        enclosed in its parentheses,  namely the variable  "number".
        This  is  the name we prefer to call the variable passed  to
        the  function when we are in the function.   We can call  it
        anything  we wish as long as it follows the rules of  naming
        an identifier.   Since the function must know what type  the
        variable  is,  it is defined following the function name but
        before the opening brace of the function itself.   Thus, the
        line  containing "int number;" tells the function  that  the
        value  passed to it will be an integer type variable.   With
        all of that out of the way,  we now have the value of  index
        from  the main program passed to the function "square",  but
        renamed "number", and available for use within the function.
        This  is the "classic" style of defining function  variables
        and has been in use since C was originally defined.  A newer
        method is gaining in popularity due to its many benefits and
        will be discussed later in this chapter.

             Following the opening brace of the function,  we define
        another  variable "numsq" for use only within  the  function
        itself,  (more  about  that  later)  and  proceed  with  the
        required  calculations.   We set "numsq" equal to the square
        of  number,  then add numsq to the current total  stored  in
        "sum".   Remember  that "sum += numsq" is the same as "sum =
        sum + numsq" from the last lesson.   We print the number and
        its square, and return to the main program.

                  MORE ABOUT PASSING A VALUE TO A FUNCTION

             When we passed the value of "index" to the function,  a
        little  more  happened  than meets  the  eye.   We  did  not
        actually  pass  the  value  of index  to  the  function,  we
        actually  passed  a  copy of the value.   In  this  way  the
        original value is protected from accidental corruption by  a


                                  Page 30









            Chapter 5 - Functions, variables, and prototypes


        called  function.   We  could  have  modified  the  variable
        "number" in any way we wished in the function "square",  and
        when we returned to the main program, "index" would not have
        been  modified.   We thus protect the value of a variable in
        the main program from being accidentally corrupted,  but  we
        cannot  return  a value to the main program from a  function
        using this technique.  We will find a well defined method of
        returning  values  to  the main program or  to  any  calling
        function  when we get to arrays and another method  when  we
        get  to pointers.   Until then the only way you will be able
        to  communicate  back to the calling function will  be  with
        global  variables.    We  have  already  hinted  at   global
        variables  above,  and will discuss them in detail later  in
        this chapter.

             Continuing  in  the main program,  we come to the  last
        function call, the call to "ending".  This call simply calls
        the last function which has no local variables defined.   It
        prints out a message with the value of "sum" contained in it
        to  end the program.   The program ends by returning to  the
        main  program and finding nothing else to do.   Compile  and
        run this program and observe the output.

                        NOW TO CONFESS A LITTLE LIE


                                          /* Chapter 5 - Program 2 */
main()  /* This is the main program */
{
int x,y;

   for(x = 0;x <= 7;x++) {
      y = squ(x);  /* go get the value of x*x */
      printf("The square of %d is %d\n",x,y);
   }

   for (x = 0;x <= 7;++x)
      printf("The value of %d is %d\n",x,squ(x));
}

squ(in)  /* function to get the value of in squared */
int in;
{
int square;

   square = in * in;
   return(square);  /* This sets squ() = square */
}


             I told you a short time ago that the only way to get  a
        value  back to the main program was through use of a  global
        variable,  but  there  is another way which we will  discuss
        after  you load and display the file  named  SQUARES.C (above).   In
        this  file we will see that it is simple to return a  single
        value  from a called function to the calling function.   But
        once again,  it is true that to return more than one  value,
        we will need to study either arrays or pointers.

             In the main program, we define two integers and begin a
        "for"  loop  which  will be executed  8  times.   The  first
        statement of the "for" loop is "y = squ(x);", which is a new
        and rather strange looking construct.  From past experience,
        we  should have no trouble understanding that  the  "squ(x)"
        portion  of  the statement is a call to the  "squ"  function
        taking along the value of "x" as a variable.  Looking  ahead
        to the function itself we find that the function prefers  to
        call  the variable "in" and it proceeds to square the  value
        of  "in" and call the result "square".  Finally, a new  kind
        of  a statement appears, the "return" statement.  The  value
        within  the parentheses is assigned to the  function  itself
        and  is  returned  as a usable value in  the  main  program.
        Thus,  the function call "squ(x)" is assigned the  value  of
        the square and returned to the main program such that "y" is
        then  set  equal  to  that value.   If  "x"  were  therefore



                                  Page 31









            Chapter 5 - Functions, variables, and prototypes


        assigned  the value 4 prior to this call, "y" would then  be
        set to 16 as a result of this line of code.

             Another  way  to  think  of this  is  to  consider  the
        grouping  of characters "squ(x)" as another variable with  a
        value  that is the square of "x",  and this new variable can
        be used any place it is legal to use a variable of its type.
        The values of "x" and "y" are then printed out.

             To  illustrate  that the grouping of  "squ(x)"  can  be
        thought  of as just another variable,  another "for" loop is
        introduced in which the function call is placed in the print
        statement rather than assigning it to a new variable.

             One  last  point must be made,  the  type  of  variable
        returned must be defined in order to make sense of the data,
        but the compiler will default the type to integer if none is
        specified.   If  any  other  type is  desired,  it  must  be
        explicitly defined.   How to do this will be demonstrated in
        the next example program.

             Compile  and  run  this program  which  also  uses  the
        "classic" method of defining function variables.

                            FLOATING POINT FUNCTIONS

                                         /* Chapter 5 - Program 3 */
float z;   /* This is a global variable */

main()
{
int index;
float x,y,sqr(),glsqr();

   for (index = 0;index <= 7;index++){
      x = index;  /* convert int to float */
      y = sqr(x); /* square x to a floating point variable */
      printf("The square of %d is %10.4f\n",index,y);
   }

   for (index = 0; index <= 7;index++) {
      z = index;
      y = glsqr();
      printf("The square of %d is %10.4f\n",index,y);
   }
}

float sqr(inval)  /* square a float, return a float */
float inval;
{
float square;

   square = inval * inval;
   return(square);
}

float glsqr()    /* square a float, return a float */
{
   return(z*z);
}


             Load the program FLOATSQ.C (above) for an example of a function
        with a floating point type of return.  It begins by defining
        a global floating point variable we will use later.  Then in
        the  "main"  part  of the program,  an integer  is  defined,
        followed  by two floating point variables,  and then by  two
        strange  looking definitions.   The expressions "sqr()"  and
        "glsqr()"  look like function calls and they are.   This  is
        the proper way in C to define that a function will return  a
        value that is not of the type "int", but of some other type,
        in  this case "float".   This tells the compiler that when a
        value  is returned from either of these  two  functions,  it
        will be of type "float".  This is, once again, the "classic"
        method of defining functions.

             Now  refer to the function "sqr" near the center of the
        listing and you will see that the function name is  preceded
        by the name "float".   This is an indication to the compiler
        that  this  function will return a value of type "float"  to
        any program that calls it.   The function is now  compatible
        with  the call to it.   The line following the function name
        contains  "float inval;",  which indicates to  the  compiler
        that  the variable passed to this function from the  calling
        program will be of type "float".




                                  Page 32









            Chapter 5 - Functions, variables, and prototypes


             The next function,  namely "glsqr",  will also return a
        "float"  type  variable,  but it uses a global variable  for
        input.   It  also does the squaring right within the  return
        statement  and  therefore has no need to define  a  separate
        variable to store the product.

             The  overall structure of this program should  pose  no
        problem and will not be discussed in any further detail.  As
        is customary with all example programs, compile and run this
        program.

               THERE IS A BUG IN THE FIRST VERSION OF TURBO C

             When you run this program, if you are using version 1.0
        of  Turbo C, you will find that the function  named  "sqr()"
        will return a value of zero.  This is because it receives  a
        zero  from  the  calling program.  The  program  is  written
        correctly  but  the  compiler has a bug in it.   A  call  to
        Borland  resulted  in  a fix by simply  using  the  "modern"
        method  of  function definition rather  than  the  "classic"
        which has been used to this point.  As you read articles  on
        C, you will see programs written in the "classic" style,  so
        you need to be capable of reading them.  It would be  highly
        recommended,  however, that you learn and use  the  "modern"
        method  which will be covered shortly in this tutorial.   We
        will  return to this program and show how to fix it so  that
        it will work in spite of the bug.

                             SCOPE OF VARIABLES

                                          /* Chapter 5 - Program 4 */
#include "stdio.h"      /* Prototypes for Input/Output             */
void head1(void);       /* Prototype for head1                     */
void head2(void);       /* Prototype for head2                     */
void head3(void);       /* Prototype for head3                     */

int count;              /* This is a global variable               */

main()
{
register int index; /* This variable is available only in main */

   head1();
   head2();
   head3();
                      /* main "for" loop of this program */
   for (index = 8;index > 0;index--) {
      int stuff;  /* This variable is only available in these braces*/
      for (stuff = 0;stuff <= 6;stuff++)
         printf("%d ",stuff);
      printf(" index is now %d\n",index);
    }
}

int counter;      /* This is available from this point on */
void head1(void)
{
int index;        /* This variable is available only in head1 */

   index = 23;
   printf("The header1 value is %d\n",index);
}

void head2(void)
{
int count;  /* This variable is available only in head2 */
            /* and it displaces the global of the same name */

   count = 53;
   printf("The header2 value is %d\n",count);
   counter = 77;
}

void head3(void)
{
   printf("The header3 value is %d\n",counter);
}


             Load the next program,  SCOPE.C (above),  and display it for  a
        discussion  of the scope of variables in a program.   Ignore
        the 4 statements in lines 2 through 5 of this program for  a
        few moments, and we will discuss them later.

             The first variable defined is a global variable "count"
        which  is available to any function in the program since  it
        is defined before any of the functions.   In addition, it is
        always  available  because  it does not come and go  as  the
        program  is  executed.   (That  will  make  sense  shortly.)
        Farther down in the program,  another global variable  named
        "counter"  is  defined  which  is also  global  but  is  not
        available  to the main program since it is defined following
        the main program.  A global variable is any variable that is
        defined  outside of any function.   Note that both of  these
        variables  are sometimes referred to as  external  variables
        because they are external to any functions.

             Return  to  the  main  program and  you  will  see  the
        variable  "index"  defined as an integer.   Ignore the  word
        "register" for the moment.   This variable is only available


                                  Page 33









            Chapter 5 - Functions, variables, and prototypes


        within the main program because that is where it is defined.
        In addition, it is an "automatic" variable, which means that
        it  only comes into existence when the function in which  it
        is  contained  is  invoked,  and ceases to  exist  when  the
        function  is  finished.   This  really  means  nothing  here
        because the main program is always in operation,  even  when
        it  gives  control to another function.  Another integer  is
        defined  within  the  "for"  braces,  namely  "stuff".   Any
        pairing  of braces can contain a variable  definition  which
        will  be  valid  and  available only while  the  program  is
        executing statements within those braces.  The variable will
        be  an  "automatic" variable and will cease  to  exist  when
        execution leaves the braces.   This is convenient to use for
        a loop counter or some other very localized variable.

                       MORE ON "AUTOMATIC" VARIABLES

             Observe  the  function named "head1" in line  26  which
        looks  a  little funny because of "void" being  used  twice.
        The purpose of the uses of the word "void" will be explained
        shortly.  The  function contains a variable  named  "index",
        which  has  nothing  to  do with the  "index"  of  the  main
        program, except that both are automatic variables.  When the
        program  is  not  actually  executing  statements  in   this
        function,  this variable named "index" does not even  exist.
        When "head1" is called, the variable is generated, and  when
        "head1"  completes its task, the variable in  "head1"  named
        "index"  is eliminated completely from existence.   Keep  in
        mind  however that this does not affect the variable of  the
        same  name  in the main program, since it  is  a  completely
        separate entity.

             Automatic   variables  therefore,   are   automatically
        generated and disposed of when needed.   The important thing
        to remember is that from one call to a function to the  next
        call,  the  value of an automatic variable is not  preserved
        and must therefore be reinitialized.

                         WHAT ARE STATIC VARIABLES?

             An  additional variable type must be mentioned at  this
        point,  the "static" variable.  By putting the reserved word
        "static"  in  front  of  a  variable  declaration  within  a
        function,  the variable or variables in that declaration are
        static  variables  and will stay in existence from  call  to
        call  of  the  particular function.

             By  putting  the  same reserved word  in  front  of  an
        external variable, one outside of any function, it makes the
        variable  private  and  not accessible to use in  any  other
        file.  This implies that it is possible to refer to external


                                  Page 34









            Chapter 5 - Functions, variables, and prototypes


        variables  in other separately compiled files,  and that  is
        true.  Examples of this usage will be given in chapter 14 of
        this tutorial.

                         USING THE SAME NAME AGAIN

             Refer  to  the  function named  "head2".   It  contains
        another  definition  of the variable  named  "count".   Even
        though  "count"  has  already  been  defined  as  a   global
        variable,  it  is  perfectly all right to reuse the name  in
        this  function.   It is a completely new variable  that  has
        nothing to do with the global variable of the same name, and
        causes  the  global  variable  to  be  unavailable  in  this
        function.   This allows you to write programs using existing
        functions  without worrying about what names were  used  for
        variables in the functions because there can be no conflict.
        You  only  need to worry about the variables that  interface
        with the functions.

                        WHAT IS A REGISTER VARIABLE?

             Now  to  fulfill  a promise made earlier about  what  a
        register  variable  is.   A  computer can  keep  data  in  a
        register  or  in  memory.   A register  is  much  faster  in
        operation  than  memory  but there are  very  few  registers
        available  for the programmer to use.  If there are  certain
        variables  that are used extensively in a program,  you  can
        designate  that  those  variables  are to  be  stored  in  a
        register  if possible in order to speed up the execution  of
        the  program.  Your compiler probably allows you to use  one
        or  more  register  variables  and  will  ignore  additional
        requests  if  you  request more  than  are  available.   The
        documentation for your compiler will list how many registers
        are  available with your compiler.  It will also inform  you
        of what types of variables can be stored in a register.

                        WHERE DO I DEFINE VARIABLES?

             Now for a refinement on a general rule stated  earlier.
        When  you have variables brought to a function as  arguments
        to  the function, and you are using the "classic"  style  of
        programming, they are defined immediately after the function
        name and prior to the opening brace for the program.   Other
        variables used in the function are defined at the  beginning
        of the function, immediately following the opening brace  of
        the function, and before any executable statements.

                            WHAT IS PROTOTYPING?

             A  prototype  is a "model" of the real thing  and  when
        programming with a good up-to-date C compiler, you have  the


                                  Page 35









            Chapter 5 - Functions, variables, and prototypes


        ability  to  define  a  "model" of  each  function  for  the
        compiler.   The compiler can then use the "model"  to  check
        each of your calls to the function and determine if you have
        used  the correct number of arguments in the  function  call
        and  if they are of the correct type.  By using  prototypes,
        you  let the compiler do some additional error checking  for
        you.   The  ANSI standard for C should be released  late  in
        1987,   and  will  contain  prototyping  as  part   of   its
        recommended  standard.   Every  good C  compiler  will  have
        prototyping available, so you should learn to use it.

             Returning to lines 3, 4, and 5 in SCOPE.C, we have  the
        prototypes  for  the three functions  contained  within  the
        program.   The first "void" in each line tells the  compiler
        that  these particular functions do not return a  value,  so
        that the compiler would flag the statement index =  head1();
        as  an  error because nothing is returned to assign  to  the
        variable  "index".  The word "void" within  the  parentheses
        tells the compiler that this function requires no parameters
        and  if a variable were included, it would be an  error  and
        the  compiler would issue a warning message.  If  you  wrote
        the  statement  head1(index);, it would be  a  error.   This
        allows  you  to use type checking when programming in  C  in
        much the same manner that it is used in Pascal, Modula 2, or
        Ada.

             You should enable prototype checking with your compiler
        at this time, if it is available with your compiler.

             Line 2 of SCOPE.C tells the system to go to the include
        files  and  get the file named STDIO.H  which  contains  the
        prototypes  for the standard input and output  functions  so
        they can be checked for proper variable types.  Don't  worry
        about the "include" yet, it will be covered in detail  later
        in this tutorial.

             Compile  and run this program.

                         STANDARD FUNCTION LIBRARIES

             Every  compiler  comes  with some  standard  predefined
        functions  which  are available for  your  use.   These  are
        mostly   input/output   functions,   character  and   string
        manipulation  functions, and math functions.  We will  cover
        most  of  these  in  subsequent  chapters.   Prototypes  are
        defined  for you by the writer of your compiler for  all  of
        the  functions that are included with your compiler.  A  few
        minutes spent studying your reference Guide will give you an
        insight in where the prototypes are defined for each of  the
        functions.



                                  Page 36









            Chapter 5 - Functions, variables, and prototypes


             In addition,  most compilers have additional  functions
        predefined that are not standard but allow the programmer to
        get the most out of his particular computer.  In the case of
        the  IBM-PC and compatibles,  most of these functions  allow
        the  programmer  to use the BIOS services available  in  the
        operating system, or to write directly to the video  monitor
        or to any place in memory.  These will not be covered in any
        detail as you will be able to study these unique aspects  of
        your compiler on your own.  Many of these kinds of functions
        are used in the example programs in chapter 14.


                             WHAT IS RECURSION?

                                         /* Chapter 5 - Program 5 */
main()
{
int index;

   index = 8;
   count_dn(index);
}

count_dn(count)
int count;
{
   count--;
   printf("The value of the count is %d\n",count);
   if (count > 0)
      count_dn(count);
   printf("Now the count is %d\n",count);
}


             Recursion  is another of those  programming  techniques
        that  seem very intimidating the first time you come  across
        it,  but  if  you will load and display the example  program
        named RECURSON.C (above), we will take all of the mystery out of it.
        This  is probably the simplest recursive program that it  is
        possible  to write and it is therefore a stupid  program  in
        actual  practice,  but for purposes of illustration,  it  is
        excellent.

             Recursion  is  nothing more than a function that  calls
        itself.   It is therefore in a loop which must have a way of
        terminating.   In the program on your monitor,  the variable
        "index"  is  set to 8,  and is used as the argument  to  the
        function  "count_dn".   The function simply  decrements  the
        variable, prints it out in a message, and if the variable is
        not zero, it calls itself, where it decrements the  variable
        again, prints it, etc. etc. etc. Finally, the variable  will
        reach  zero,  and the function will not call  itself  again.
        Instead, it will return to the prior time it called  itself,
        and  return again, until finally it will return to the  main
        program and from there return to DOS.

             For  purposes  of understanding you can think of it  as
        having 8 copies of the function "count_dn" available and  it
        simply  called all of them one at a time,  keeping track  of
        which  copy it was in at any given time.   That is not  what
        actually  happened,  but it is a reasonable illustration for
        you to begin understanding what it was really doing.

                              WHAT DID IT DO?

             A  better explanation of what actually happened  is  in
        order.   When you called the function from itself, it stored
        all  of the variables and all of the internal flags it needs
        to  complete the function in a block  somewhere.   The  next
        time it called itself,  it did the same thing,  creating and
        storing  another  block of everything it needed to  complete


                                  Page 37









            Chapter 5 - Functions, variables, and prototypes


        that  function call.   It continued making these blocks  and
        storing them away until it reached the last function when it
        started  retrieving the blocks of data,  and using  them  to
        complete  each function call.   The blocks were stored on an
        internal part of the computer called the "stack".  This is a
        part  of  memory carefully organized to store data  just  as
        described above.  It is beyond the scope of this tutorial to
        describe the stack in detail,  but it would be good for your
        programming  experience to read some material describing the
        stack.   A stack is used in nearly all modern computers  for
        internal housekeeping chores.

             In using recursion,  you may desire to write a  program
        with  indirect recursion as opposed to the direct  recursion
        described  above.    Indirect  recursion  would  be  when  a
        function  "A"  calls the function "B",  which in turn  calls
        "A",  etc.   This is entirely permissible,  the system  will
        take  care of putting the necessary things on the stack  and
        retrieving  them when needed again.   There is no reason why
        you  could not have three functions calling each other in  a
        circle,  or four,  or five,  etc.   The C compiler will take
        care of all of the details for you.

             The thing you must remember about recursion is that  at
        some  point,  something  must  go to  zero,  or  reach  some
        predefined  point to terminate the loop.   If not,  you will
        have  an  infinite  loop,  and the stack will  fill  up  and
        overflow,  giving  you  an error and  stopping  the  program
        rather abruptly.

             Compile and run this program.  If you compile this with
        prototype  checking  on  you will  find  several  "warnings"
        issued  during compilation.  Study these for a few  minutes.
        One of the suggested exercises at the end of this chapter is
        to modify this program to eliminate the prototype warnings.

                        ANOTHER EXAMPLE OF RECURSION

                                         /* Chapter 5 - Program 6 */
#include "stdio.h"      /* Prototypes for standard Input/Output   */
#include "string.h"     /* Prototypes for string operations       */

void forward_and_backwards(char line_of_char[],int index);

main()
{
char line_of_char[80];
int index = 0;

   strcpy(line_of_char,"This is a string.\n");

   forward_and_backwards(line_of_char,index);

}

void forward_and_backwards(char line_of_char[],int index)
{
   if (line_of_char[index]) {
      printf("%c",line_of_char[index]);
      index++;
      forward_and_backwards(line_of_char,index);
   }
   printf("%c",line_of_char[index]);
}


             The  program  named  BACKWARD.C (above) is another  example  of
        recursion,  so load it and display it on your screen.   This
        program  is  similar to the last one except that it  uses  a
        character array.  Each successive call to the function named
        "forward_and_backwards" causes one character of the  message
        to  be printed.  Additionally, each time the function  ends,
        one of the characters is printed again, this time  backwards
        as the string of recursive function calls is retraced.

             This  program  uses  the "modern"  method  of  function
        definition  and  includes full prototype  definitions.   The
        "modern"  method of function definition moves the  types  of
        the  variables into the parentheses along with the  variable


                                  Page 38









            Chapter 5 - Functions, variables, and prototypes


        names  themselves.   The  final  result  is  that  the  line
        containing   the   function  name  looks   more   like   the
        corresponding line in Pascal, Modula 2, or Ada.

             Don't worry about the character array defined in line 9
        or  the  other  new  material  presented  here.   After  you
        complete chapter 7 of this tutorial,  this program will make
        sense.   It  was  felt that introducing a second example  of
        recursion was important so this file is included here.

             Compile  and run this program with  prototype  checking
        enabled and observe the results.

                 HOW TO WORK AROUND THE TURBO C (v1.0) BUG


                                         /* Chapter 5 - Program 7 */
#include "stdio.h"   /* Prototypes for standard Input/Outputs     */

float sqr(float inval);
float glsqr(void);

float z;   /* This is a global variable */

main()
{
int index;
float x,y;

   for (index = 0;index <= 7;index++){
      x = index;  /* convert int to float */
      y = sqr(x); /* square x to a floating point variable */
      printf("The square of %d is %10.4f\n",index,y);
   }

   for (index = 0; index <= 7;index++) {
      z = index;
      y = glsqr();
      printf("The square of %d is %10.4f\n",index,y);
   }
}

float sqr(float inval)  /* square a float, return a float */
{
float square;

   square = inval * inval;
   return(square);
}

float glsqr(void)    /* square a float, return a float */
{
   return(z*z);
}

             If you are using Turbo C version 1.00, load and display
        the  program named FLOATSQ2.C (above) which is an exact copy of  the
        program   FLOATSQ.C   which  we  considered   earlier   with
        prototyping added.  The return of the erroneous zeros is now
        repaired  and the correct values are returned.   Apparently,
        when  the  coders of Turbo C at Borland  were  testing  this
        compiler,  they used prototyping and never found  this  bug.
        The  use  of  prototyping  is a  good  practice  for  all  C
        programmers to get into.

             Several things should be mentioned about this  program.
        First, the word "float" at the beginning of lines 27 and  35
        indicate to the compiler that these functions are  functions
        that return "float" type values.  Also, since the prototypes
        are  given before "main", the functions are not required  to
        be identified in line 12 as they were in line 7 of FLOATSQ.C
        earlier  in this chapter.  They can be included in line  12,
        but they are not required to be.

             Notice  also that the type of the variable  "inval"  is
        included  within  the parentheses in line 27.  It  would  be
        very educational for you to modify this program so that  you
        included  a  call  to "sqr" with a variable  of  type  "int"
        within  the  parentheses to see what kind of a  warning  you
        would  get.   Do  the  same thing  in  the  program  without
        prototype checking, FLOATSQ.C.












                                  Page 39









            Chapter 5 - Functions, variables, and prototypes


        PROGRAMMING EXERCISES

        1.   Rewrite  TEMPCONV.C,  from an earlier chapter, and move
             the temperature calculation to a function.

        2.   Write a program that writes your name on the monitor 10
             times by calling a function to do the writing. Move the
             called function ahead of the "main" function to see  if
             your C compiler will allow it.

        3.   Add  prototyping  to the program  named  RECURSON.C  to
             eliminate the warnings.





                                  Page 40

TOP

C tutorial (chapter 4)
Chapter 4 - Assignment & Logical compares


             Throughout  this  chapter,  references  are  given   to
        various  ranges  of  variables.  Your  compiler  may  use  a
        different range for some of the variables since the proposed
        ANSI  standard does not define specific limits for all  data
        types.  Consult the documentation for your compiler for  the
        exact range for each of the variable types.

                       INTEGER ASSIGNMENT STATEMENTS

                                         /* Chapter 4 - Program 1 */
/* This program will illustrate the assignment statements         */

main()
{
int a,b,c;    /* Integer variables for examples */

   a = 12;
   b = 3;
   c = a + b;          /* simple addition */
   c = a - b;          /* simple subtraction */
   c = a * b;          /* simple multiplication */
   c = a / b;          /* simple division */
   c = a % b;          /* simple modulo (remainder) */
   c = 12*a + b/2 - a*b*2/(a*c + b*2);
   c = c/4+13*(a + b)/3 - a*b + 2*a*a;
   a = a + 1;          /* incrementing a variable */
   b = b * 5;

   a = b = c = 20;     /* multiple assignment */
   a = b = c = 12*13/4;
}



             Load the file INTASIGN.C (above) and display it for an  example
        of  assignment statements.   Three variables are defined for
        use  in the program and the rest of the program is merely  a
        series of illustrations of various assignments.   The  first
        two  lines  of  the assignment statements  assign  numerical
        values  to "a" and "b",  and the next five lines  illustrate
        the  five  basic arithmetic functions and how to  use  them.
        The  fifth is the modulo operator and gives the remainder if
        the two variables were divided.   It can only be applied  to
        "int"  or  "char"  type  variables,   and  of  course  "int"
        extensions such as "long",  "short",  etc.  Following these,
        there  are two lines illustrating how to combine some of the
        variables  in  some complex math expressions.   All  of  the
        above examples should require no comment except to say  that
        none  of the equations are meant to be  particularly  useful
        except  as illustrations.  The "char" type variable will  be
        defined in the description of the next example program.

             The  expressions  in  lines 17  and  18  are  perfectly
        acceptable  as given, but we will see later in this  chapter
        that  there is another way to write these for  more  compact
        code.

             This leaves us with the last two lines which may appear
        to  you  as being very strange.   The C compiler  scans  the
        assignment  statement from right to left,  (which may seem a
        bit odd since we do not read that way),  resulting in a very
        useful construct,  namely the one given here.   The compiler
        finds the value 20, assigns it to "c", then continues to the
        left finding that the latest result of a calculation  should
        be  assigned to "b".   Thinking that the latest  calculation
        resulted in a 20,  it assigns it to "b" also,  and continues
        the leftward scan assigning the value 20 to "a" also.   This
        is a very useful construct when you are initializing a group
        of  variables.   The last statement illustrates that  it  is
        possible  to actually do some calculations to arrive at  the
        value  which  will be assigned to all three  variables.   In
        fact,  the rightmost expression can contain variables,  even
        "a", "b", & "c".

             The  program has no output, so compiling and  executing
        this  program  will be very uninteresting.  Since  you  have


                                  Page 18









                 Chapter 4 - Assignment & Logical compares


        already  learned how to display some integer  results  using
        the "printf" function, it would be to your advantage to  add
        some output statements to this program to see if the various
        statements do what you think they should do.

             This would be a good time for a preliminary  definition
        of  a  rule to be followed in C.   The data definitions  are
        always given before any executable statements in any program
        block.   This is why the variables are defined first in this
        program and in every C program.  If you try to define a  new
        variable after executing some statements, your compiler will
        issue an error.

                           ADDITIONAL DATA TYPES


                                         /* Chapter 4 - Program 2 */
/* The purpose of this file is to introduce additional data types */

main()
{
int a,b,c;            /* -32768 to 32767 with no decimal point */
char x,y,z;           /* -128 to 127 with no decimal point     */
float num,toy,thing;  /* 3.4E-38 to 3.4E+38 with decimal point */

   a = b = c = -27;
   x = y = z = 'A';
   num = toy = thing = 3.6792;

   a = y;           /* a is now 65 (character A)               */
   x = b;           /* x is now -27                            */
   num = b;         /* num will now be -27.00                  */
   a = toy;         /* a will now be 3                         */

}


             Loading and editing MORTYPES.C (above) will illustrate how some
        additional  data  types can be used.   Once  again  we  have
        defined  a  few integer type variables which you  should  be
        fairly  familiar  with  by now,  but we have added  two  new
        types, the "char", and the "float".

             The  "char"  type  of data is nearly the  same  as  the
        integer except that it can only be assigned numerical values
        between  -128 to 127 on most implementations of C, since  it
        is  stored in only one byte of memory.  The "char"  type  of
        data is usually used for ASCII data, more commonly known  as
        text.  The text you are reading was originally written on  a
        computer with a word processor that stored the words in  the
        computer  one character per byte.  In contrast, the  integer
        data  type  is  stored in two bytes of  computer  memory  on
        nearly all microcomputers.

                              DATA TYPE MIXING

             It  would be profitable at this time to discuss the way
        C handles the two types "char" and "int".  Most functions in
        C  that are designed to operate with integer type  variables
        will work equally well with character type variables because
        they  are a form of an integer variable.   Those  functions,
        when called on to use a "char" type variable,  will actually
        promote  the "char" data into integer data before using  it.
        For this reason, it is possible to mix "char" and "int" type
        variables in nearly any way you desire.   The compiler  will
        not get confused,  but you might.  It is good not to rely on
        this too much, but to carefully use only the proper types of
        data where they should be used.

              The second new data type is the "float" type of  data,
        commonly  called floating point data.  This is a  data  type
        which  usually  has a very large range, a  large  number  of
        significant digits, and a large number of computer words are


                                  Page 19









                 Chapter 4 - Assignment & Logical compares


        required  to store it.  The "float" data type has a  decimal
        point associated with it and has an allowable range of  from
        3.4E-38   to  3.4E+38  when  using  most  C   compilers   on
        microcomputers,  and  is  composed of  about  7  significant
        digits.

                       HOW TO USE THE NEW DATA TYPES

             The  first three lines of the program assign values  to
        all nine of the defined variables so we can manipulate  some
        of the data between the different types.

             Since,  as  mentioned above,  a "char" data type is  in
        reality  an "integer" data type,  no special  considerations
        need be taken to promote a "char" to an "int",  and a "char"
        type data field can be assigned to an "int" variable.   When
        going the other way, there is no standard, so you may simply
        get garbage if the value of the integer variable is  outside
        the  range of the "char" type variable.   It will  translate
        correctly if the value is within the range of -128 to 127.

             The   third   line   illustrates  the   simplicity   of
        translating an integer into a "float",  simply assign it the
        new  value  and the system will do  the  proper  conversion.
        When  going  the  other  way  however,  there  is  an  added
        complication.   Since  there may be a fractional part of the
        floating  point number,  the system must decide what  to  do
        with it.  By definitions , it will truncate it.

             This program produces no output, and we haven't covered
        a way to print out "char" and "float" type variables, so you
        can't  really  get  in  to this program and  play  with  the
        results, but the next program will cover this for you.

             Compile  and  run  this program.

                          LOTS OF VARIABLE TYPES


                                         /* Chapter 4 - Program 3 */
main()
{
int a;              /* simple integer type                  */
long int b;         /* long integer type                    */
short int c;        /* short integer type                   */
unsigned int d;     /* unsigned integer type                */
char e;             /* character type                       */
float f;            /* floating point type                  */
double g;           /* double precision floating point      */

   a = 1023;
   b = 2222;
   c = 123;
   d = 1234;
   e = 'X';
   f = 3.14159;
   g = 3.1415926535898;

   printf("a = %d\n",a);      /* decimal output             */
   printf("a = %o\n",a);      /* octal output               */
   printf("a = %x\n",a);      /* hexadecimal output         */
   printf("b = %ld\n",b);     /* decimal long output        */
   printf("c = %d\n",c);      /* decimal short output       */
   printf("d = %u\n",d);      /* unsigned output            */
   printf("e = %c\n",e);      /* character output           */
   printf("f = %f\n",f);      /* floating output            */
   printf("g = %f\n",g);      /* double float output        */
   printf("\n");
   printf("a = %d\n",a);      /* simple int output          */
   printf("a = %7d\n",a);     /* use a field width of 7     */
   printf("a = %-7d\n",a);    /* left justify in field of 7 */
   c = 5;
   d = 8;
   printf("a = %*d\n",c,a);   /* use a field width of 5     */
   printf("a = %*d\n",d,a);   /* use a field width of 8     */
   printf("\n");
   printf("f = %f\n",f);      /* simple float output        */
   printf("f = %12f\n",f);    /* use field width of 12      */
   printf("f = %12.3f\n",f);  /* use 3 decimal places       */
   printf("f = %12.5f\n",f);  /* use 5 decimal places       */
   printf("f = %-12.5f\n",f); /* left justify in field      */
}


             Load the file LOTTYPES.C (above) and display it on your screen.
        This  file contains nearly every standard simple  data  type
        available  in the programming language C.  There  are  other
        types,  but  they are the compound types (ie  -  arrays  and
        structures) that we will cover in due time.

             Observe  the  file.  First we define  a  simple  "int",
        followed by a "long int" which has a range of -2147483648 to
        2147483647  with most C compilers, and a "short  int"  which
        has  a  range  that  is identical  to  that  for  the  "int"
        variable, namely -32768 to 32767. The "unsigned" is next and
        is  defined as the same size as the "int" but with no  sign.
        The  "unsigned" then will cover a range of 0 to  65535.   It


                                  Page 20









                 Chapter 4 - Assignment & Logical compares


        should  be  pointed out that when the  "long",  "short",  or
        "unsigned" is desired, the "int" is optional and is left out
        by  most experienced programmers.  We have  already  covered
        the "char" and the "float", which leaves only the  "double".
        The "double" covers a greater range than the "float" and has
        more  significant digits for more precise calculations.   It
        also  requires more memory to store a value than the  simple
        "float". The "double" in most C compilers covers a range  of
        1.7E-308 to 1.7E+308.

             Note that other compunding of types can be done such as
        "long  unsigned  int",  "unsigned char",  etc.   Check  your
        documentation for a complete list of variable types.

             Another  diversion  is in order at  this  point.   Your
        compiler has no provisions for floating point math, but only
        double floating point math.  It will promote a "float" to  a
        "double"  before doing calculations and therefore  only  one
        math  library  will be needed.  Of course, this  is  totally
        transparent  to  you, so you don't need to worry  about  it.
        Because  of  this, you may think that it would  be  best  to
        simply define every floating point variable as double, since
        they  are promoted before use in any calculations, but  that
        may not be a good idea.  A "float" variable requires 4 bytes
        of storage and a "double" requires 8 bytes of storage, so if
        you have a large volume of floating point data to store, the
        "double" will obviously require much more memory.

             After  defining  the data types in  the  program  under
        consideration, a numerical value is assigned to each of  the
        defined  variables  in  order to demonstrate  the  means  of
        outputting each to the monitor.

                            THE CONVERSION CHARACTERS

             Following   is  a  list  of  some  of  the   conversion
        characters  and  the  way  they are  used  in  the  "printf"
        statement.   A  complete  list  of  all  of  the  conversion
        characters  should  be included with the  documentation  for
        your compiler.

             d    decimal notation
             o    octal notation
             x    hexadecimal notation
             u    unsigned notation
             c    character notation
             s    string notation
             f    floating point notation





                                  Page 21









                 Chapter 4 - Assignment & Logical compares


             Each  of  these  is used following a  percent  sign  to
        indicate  the type of output conversion,  and between  those
        two characters, the following  fields may be added.

             -    left justification in its field
             (n)  a number specifying minimum field width
             .    to separate n from m
             (m)  significant fractional digits for a float
             l    to indicate a "long"

             These  are all used in the examples which are  included
        in the program presently displayed on your monitor, with the
        exception of the string notation which will be covered later
        in  this tutorial. Note especially the variable field  width
        specification  demonstrated in lines 33 to 36.  This is  not
        part of the original definition of C, but it is included  in
        the  proposed  ANSI standard and will become part of  the  C
        language.   Compile and run this program to see what  effect
        the various fields have on the output.

             You  now  have the ability to display any of  the  data
        fields  in  the  previous programs and it would be  to  your
        advantage to go back and see if you can display some of  the
        fields anyway you desire.

                              LOGICAL COMPARES

                                          /* Chapter 4 - Program 4 */
main()  /* This file will illustrate logical compares */
{
int x = 11,y = 11,z = 11;
char a = 40,b = 40,c = 40;
float r = 12.987,s = 12.987,t = 12.987;

                         /* First group of compare statements */

   if (x == y) z = -13;   /* This will set z = -13 */
   if (x > z)  a = 'A';   /* This will set a = 65  */
   if (!(x > z)) a = 'B'; /* This will change nothing */
   if (b <= c) r = 0.0;   /* This will set r = 0.0 */
   if (r != s) t = c/2;   /* This will set t = 20  */

                         /* Second group of compare statements */

   if (x = (r != s)) z = 1000; /* This will set x = some positive
                                  number and z = 1000 */
   if (x = y) z = 222;   /* This sets x = y, and z = 222 */
   if (x != 0) z = 333;  /* This sets z = 333 */
   if (x) z = 444;       /* This sets z = 444 */

                         /* Third group of compare statements */

   x = y = z = 77;
   if ((x == y) && (x == 77)) z = 33; /* This sets z = 33 */
   if ((x > y) || (z > 12))   z = 22; /* This sets z = 22 */
   if (x && y && z) z = 11;           /* This sets z = 11 */
   if ((x = 1) && (y = 2) && (z = 3)) r = 12.00; /* This sets
                             x = 1, y = 2, z = 3, r = 12.00 */
   if ((x == 2) && (y = 3) && (z = 4)) r = 14.56; /* This doesn't
                             change anything */

                         /* Fourth group of compares */

   if (x == x); z = 27.345;  /* z always gets changed */
   if (x != x)  z = 27.345;  /* Nothing gets changed */
   if (x = 0)   z = 27.345;  /* This sets x = 0, z is unchanged */

}


             Load  and  view  the file  named  COMPARES.C (above)  for  many
        examples  of compare statements in C.   We begin by defining
        and  initializing  nine variables to use  in  the  following
        compare  statements.   This initialization is new to you and
        can be used to initialize variables while they are defined.

             The  first  group of compare statements represents  the
        simplest  kinds  of compares since they simply  compare  two
        variables.    Either  variable  could  be  replaced  with  a
        constant and still be a valid compare,  but two variables is
        the general case.  The first compare checks to see if "x" is
        equal  to  "y"  and it uses the double equal  sign  for  the
        comparison.   A single equal sign could be used here but  it
        would have a different meaning as we will see shortly.   The
        second  comparison checks to see if "x" is greater than "z".

             The  third compare introduces the "NOT"  operator,  the
        exclamation,  which can be used to invert the result of  any
        logical  compare.   The fourth checks for "b" less  than  or
        equal to "c", and the last checks for "r" not equal to  "s".
        As  we  learned in the last chapter, if the  result  of  the
        compare  is  true, the statement following the  "if"  clause
        will be executed and the results are given in the  comments.



                                  Page 22









                 Chapter 4 - Assignment & Logical compares


        Note  that  "less than" and "greater than or equal  to"  are
        also available, but are not illustrated here.

             It  would be well to mention the different format  used
        for the "if" statement in this example program.   A carriage
        return  is  not  required as a statement  separator  and  by
        putting the conditional clause on the same line as the "if",
        it adds to the readability of the overall program.

                               MORE COMPARES

             The  compares  in  the  second group  are  a  bit  more
        involved.  Starting with the first compare, we find a rather
        strange  looking set of conditions in the  parentheses.   To
        understand  this  we must understand just what a  "true"  or
        "false"  is in the C language.   A "false" is defined  as  a
        value  of zero,  and "true" is defined as a non-zero  value.
        Any  integer  or char type of variable can be used  for  the
        result of a true/false test, or the result can be an implied
        integer or char.

             Look  at  the  first compare of  the  second  group  of
        compare  statements.  The expression "r != s" will  evaluate
        as  a "true" since "r" was set to 0.0 above, so  the  result
        will  be a non-zero value.  With most C compilers, it  would
        always  be set to a 1, but you could get in trouble  if  you
        wrote  a program that depended on it being 1 in  all  cases.
        Good programming practice would be to not use the  resulting
        1  in any calculations.  Even though the two variables  that
        are  compared are "float" variables, the result will  be  of
        type  "integer".  There is no explicit variable to which  it
        will be assigned so the result of the compare is an  implied
        integer.   Finally the resulting number, probably 1 in  this
        case,  is assigned to the integer variable "x".   If  double
        equal signs were used, the phantom value, namely 1, would be
        compared  to  the value of "x", but since the  single  equal
        sign  is  used, the value 1 is simply assigned  to  "x",  as
        though  the  statement were not  in  parentheses.   Finally,
        since  the result of the assignment in the  parentheses  was
        non-zero, the entire expression is evaluated as "true",  and
        "z" is assigned the value of 1000.  Thus we accomplished two
        things  in  this  statement, we assigned "x"  a  new  value,
        probably  1,  and  we assigned "z" the value  of  1000.   We
        covered a lot in this statement so you may wish to review it
        before  going on.  The important things to remember are  the
        values  that  define "true" and "false", and the  fact  that
        several  things can be assigned in a conditional  statement.
        The  value  assigned to "x" was probably a 1,  but  remember
        that the only requirement is that it is nonzero.




                                  Page 23









                 Chapter 4 - Assignment & Logical compares


             The next example should help clear up some of the above
        in your mind.  In this example, "x" is assigned the value of
        "y",  and since the result is 11, the condition is non-zero,
        which is true, and the variable "z" is assigned 222.

             The third example, in the second group, compares "x" to
        zero.   If  the result is true,  meaning that if "x" is  not
        zero,  then "z" is assigned the value of 333,  which it will
        be.   The  last  example in this group illustrates the  same
        concept,  since the result will be true if "x" is  non-zero.
        The compare to zero is not actually needed and the result of
        the compare is true.   The third and fourth examples of this
        group are therefore identical.

                        ADDITIONAL COMPARE CONCEPTS

             The   third  group  of  compares  will  introduce  some
        additional  concepts,  namely  the  logical  "AND"  and  the
        logical  "OR".   We  assign  the value of 77  to  the  three
        integer  variables  simply to get started  again  with  some
        defined  values.   The  first  compare of  the  third  group
        contains  the new control "&&",  which is the logical "AND".
        The  entire statement reads,  if "x" equals "y" AND  if  "x"
        equals  77 then the result is "true".   Since this is  true,
        the variable z is set equal to 33.

             The  next  compare  in this group introduces  the  "||"
        operator which is the "OR".   The statement reads, if "x" is
        greater  than  "y"  OR if "z" is greater than  12  then  the
        result is true.   Since "z" is greater than 12,  it  doesn't
        matter  if "x" is greater than "y" or not,  because only one
        of  the  two conditions must be true for the  result  to  be
        true.  The result is true, so therefore "z" will be assigned
        the value of 22.

                             LOGICAL EVALUATION

             When a compound expression is evaluated, the evaluation
        proceeds from left to right and as soon as the result of the
        outcome is assured,  evaluation stops.   Namely, in the case
        of  an "AND" evaluation,  when one of the terms evaluates to
        "false",  evaluation is discontinued because additional true
        terms  cannot make the result ever become  "true".   In  the
        case of an "OR" evaluation,  if any of the terms is found to
        be  "true",  evaluation stops because it will be  impossible
        for additional terms to cause the result to be "false".   In
        the case of additionally nested terms,  the above rules will
        be applied to each of the nested levels.





                                  Page 24









                 Chapter 4 - Assignment & Logical compares


                          PRECEDENCE OF OPERATORS

             The  question will come up concerning the precedence of
        operators.   Which  operators are evaluated first and  which
        last?   There are many rules about this topic, but  I  would
        suggest  that  you  don't  worry about  it  at  this  point.
        Instead,  use  lots  of  parentheses  to  group   variables,
        constants,  and  operators  in  a  way  meaningful  to  you.
        Parentheses always have the highest priority and will remove
        any  question of which operations will be done first in  any
        particular statements.

             Going  on to the next example in group three,  we  find
        three  simple variables used in the conditional part of  the
        compare.   Since  all  three  are non-zero,  all  three  are
        "true",  and therefore the "AND" of the three variables  are
        true,  leading  to  the result being "true",  and "z"  being
        assigned  the value of 11.   Note that since the  variables,
        "r", "s", and "t" are "float" type variables, they could not
        be  used this way,  but they could each be compared to  zero
        and the same type of expression could be used.

             Continuing on to the fourth example of the third  group
        we  find three assignment statements in the compare part  of
        the "if" statement.  If you understood the above discussion,
        you  should have no difficulty understanding that the  three
        variables are assigned their respective new values,  and the
        result  of  all three are non-zero,  leading to a  resulting
        value of "TRUE".

                        THIS IS A TRICK, BE CAREFUL

             The last example of the third group contains a bit of a
        trick, but since we have covered it above, it is nothing new
        to you.  Notice that the first part of the compare evaluates
        to  "FALSE".   The  remaining parts of the compare  are  not
        evaluated,  because it is an "AND" and it will definitely be
        resolved as a "FALSE" because the first term is  false.   If
        the program was dependent on the value of "y" being set to 3
        in  the  next  part of the compare,  it  will  fail  because
        evaluation  will  cease following the "FALSE" found  in  the
        first  term.   Likewise,  "z" will not be set to 4,  and the
        variable "r" will not be changed.

                          POTENTIAL PROBLEM AREAS

             The   last   group   of   compares   illustrate   three
        possibilities for getting into a bit of trouble.   All three
        have  the  common  result that "z" will not get set  to  the
        desired value,  but for different reasons.   In the case  of
        the  first  one,  the compare evaluates as "true",  but  the


                                  Page 25









                 Chapter 4 - Assignment & Logical compares


        semicolon  following the second parentheses  terminates  the
        "if"  clause,  and the assignment statement involving "z" is
        always executed as the next statement.   The "if"  therefore
        has  no  effect  because of the  misplaced  semicolon.   The
        second  statement is much more straightforward  because  "x"
        will  always  be equal to itself,  therefore the  inequality
        will never be true, and the entire statement will never do a
        thing, but is wasted effort.  The last statement will always
        assign  0  to "x" and the compare will therefore  always  be
        "false",  never  executing the conditional part of the  "if"
        statement.

             The  conditional  statement is extremely important  and
        must be thoroughly understood to write efficient C programs.
        If  any  part of this discussion is unclear  in  your  mind,
        restudy  it  until you are confident that you understand  it
        thoroughly before proceeding onward.

             Compile and run this program.  Add some printout to see
        the results of some of the operations.

                           THE CRYPTIC PART OF C

                                         /* Chapter 4 - Program 5 */
main()
{
int x = 0,y = 2,z = 1025;
float a = 0.0,b = 3.14159,c = -37.234;

                                              /* incrementing */
   x = x + 1;       /* This increments x */
   x++;             /* This increments x */
   ++x;             /* This increments x */
   z = y++;         /* z = 2, y = 3 */
   z = ++y;         /* z = 4, y = 4 */

                                              /* decrementing */
   y = y - 1;       /* This decrements y */
   y--;             /* This decrements y */
   --y;             /* This decrements y */
   y = 3;
   z = y--;         /* z = 3, y = 2 */
   z = --y;         /* z = 1, y = 1 */

                                              /* arithmetic op */
   a = a + 12;      /* This adds 12 to a */
   a += 12;         /* This adds 12 more to a */
   a *= 3.2;        /* This multiplies a by 3.2 */
   a -= b;          /* This subtracts b from a */
   a /= 10.0;       /* This divides a by 10.0 */

                                     /* conditional expression */
   a = (b >= 3.0 ? 2.0 : 10.5 );     /* This expression     */

   if (b >= 3.0)                     /* And this expression */
      a = 2.0;                       /* are identical, both */
   else                              /* will cause the same */
      a = 10.5;                      /* result.             */

   c = (a > b?a:b);        /* c will have the max of a or b */
   c = (a > b?b:a);        /* c will have the min of a or b */
}


             There are three constructs used in C that make no sense
        at   all  when  first  encountered  because  they  are   not
        intuitive,  but they greatly increase the efficiency of  the
        compiled  code  and  are used extensively by  experienced  C
        programmers.   You  should therefore be exposed to them  and
        learn to use them because they will appear in most,  if  not
        all,  of the programs you see in the publications.  Load and
        examine  the file named CRYPTIC.C (above) for examples of the  three
        new constructs.

             In  this  program,   some  variables  are  defined  and
        initialized in the same statements for use below.  The first
        executable statement simply adds 1 to the value of "x",  and
        should come as no surprise to you.   The next two statements
        also  add one to the value of "x",  but it is not  intuitive
        that this is what happens.   It is simply by definition that
        this is true.  Therefore, by definition of the C language, a
        double   plus  sign  either  before  or  after  a   variable
        increments  that variable by 1.   Additionally,  if the plus
        signs are before the variable,  the variable is  incremented
        before  it  is used,  and if the plus signs  are  after  the
        variable,  the variable is used,  then incremented.   In the
        next statement, the value of "y" is assigned to the variable
        "z",  then  "y"  is incremented because the plus  signs  are
        after  the  variable  "y".   In the last  statement  of  the
        incrementing  group of example statements,  the value of "y"
        is  incremented then its value is assigned to  the  variable
        "z".


                                  Page 26









                 Chapter 4 - Assignment & Logical compares



             The  next group of statements illustrate decrementing a
        variable by one.   The definition works exactly the same way
        for decrementing as it does for incrementing.   If the minus
        signs are before the variable,  the variable is decremented,
        then  used,  and if the minus signs are after the  variable,
        the variable is used, then decremented.

                      THE CRYPTIC ARITHMETIC OPERATOR

             Another  useful but cryptic operator is the  arithmetic
        operator.   This operator is used to modify any variable  by
        some constant value.  The first statement of the "arithmetic
        operator" group of statements simply adds 12 to the value of
        the variable "a".   The second statement does the same,  but
        once again, it is not intuitive that they are the same.  Any
        of the four basic functions of arithmetic, "+", "-", "*", or
        "/",  can  be handled in this way,  by putting the  function
        desired  in  front  of the equal sign  and  eliminating  the
        second  reference to the variable name.   It should be noted
        that  the  expression on the right side  of  the  arithmetic
        operator can be any valid expression,  the examples are kept
        simple for your introduction to this new operator.

             Just  like the incrementing and decrementing operators,
        the arithmetic operator is used extensively by experienced C
        programmers and it would pay you well to understand it.

                         THE CONDITIONAL EXPRESSION

             The  conditional expression is just as cryptic  as  the
        last  two,  but once again it can be very useful so it would
        pay you to understand it.   It consists of three expressions
        within parentheses separated by a question mark and a colon.
        The  expression prior to the question mark is  evaluated  to
        determine  if it is "true" or "false".   If it is true,  the
        expression  between  the  question mark  and  the  colon  is
        evaluated,  and if it is not true,  the expression following
        the  colon  is evaluated.   The result of the evaluation  is
        used for the assignment.   The final result is identical  to
        that  of an "if" statement with an "else" clause.   This  is
        illustrated  by  the  second example  in  this  group.   The
        conditional  expression  has  the added  advantage  of  more
        compact code that will compile to fewer machine instructions
        in the final program.

             The  final two lines of this example program are  given
        to  illustrate  a very compact way to assign the greater  of
        two variables "a" or "b" to "c", and to assign the lessor of
        the  same two variables to "c".   Notice how  efficient  the
        code is in these two examples.


                                  Page 27









                 Chapter 4 - Assignment & Logical compares



                     TO BE CRYPTIC OR NOT TO BE CRYPTIC

             Several students of C have stated that they didn't like
        these  three  cryptic constructs and that they would  simply
        never  use  them.  This will be fine if they never  have  to
        read  anybody  else's  program, or use  any  other  programs
        within their own.  I have found many functions that I wished
        to  use within a program but needed a small modification  to
        use  it, requiring me to understand another  person's  code.
        It  would therefore be to your advantage to learn these  new
        constructs, and use them. They will be used in the remainder
        of this tutorial, so you will be constantly exposed to them.

             This has been a long chapter but it contained important
        material  to  get  you  started in using  C.   In  the  next
        chapter,  we  will go on to the building blocks  of  C,  the
        functions.  At that point, you will have enough of the basic
        materials to allow you to begin writing meaningful programs.


        PROGRAMMING EXERCISES

        1.   Write  a program that will count from 1 to 12 and print
             the count, and its square, for each count.
                  1    1
                  2    4
                  3    9   etc.

        2.   Write a program that counts from 1 to 12 and prints the
             count  and  its  inversion  to  5  decimal  places  for
             each count. This will require a floating point number.
                  1    1.00000
                  2     .50000
                  3     .33333
                  4     .25000
                  etc.

        3.   Write a program that will count from 1 to 100 and print
             only those values between 32 and 39, one to a line.












                                  Page 28

TOP

C tutorial [chapter 3]
Chapter 3 - Program Control


                               THE WHILE LOOP

             The  C programming language has several structures  for
        looping  and conditional branching.   We will cover them all
        in this chapter and we will begin with the while loop.   The
        while  loop continues to loop while some condition is  true.
        When   the   condition  becomes  false,   the   looping   is
        discontinued.   It therefore does just what it says it does,
        the name of the loop being very descriptive.

        
****************************************

                                         /* Chapter 3 - Program 1 */
/* This is an example of a "while" loop */

main()
{
int count;

   count = 0;
   while (count < 6) {
      printf("The value of count is %d\n",count);
      count = count + 1;
   }


****************************************        

             Load the program WHILE.C and display it for an  example
        of  a while loop.   We begin with a comment and the  program
        name,  then  go  on  to define an integer  variable  "count"
        within the body of the program.  The variable is set to zero
        and we come to the while loop itself.  The syntax of a while
        loop is just as shown here.  The keyword "while" is followed
        by an expression of something in parentheses,  followed by a
        compound  statement  bracketed by braces.   As long  as  the
        expression in parenthesis is true, all statements within the
        braces will be executed.   In this case,  since the variable
        count  is incremented by one every time the  statements  are
        executed, it will eventually reach 6, the statement will not
        be executed,  and the loop will be terminated.   The program
        control   will   resume  at  the  statement  following   the
        statements in braces.

             We  will  cover  the compare  expression,  the  one  in
        parentheses, in the next chapter.  Until then, simply accept
        the  expressions for what you think they should do  and  you
        will probably be correct.

             Several  things must be pointed out regarding the while
        loop.   First,  if the variable count were initially set  to
        any  number greater than 5,  the statements within the  loop
        would  not be executed at all,  so it is possible to have  a
        while  loop  that  never  is  executed.   Secondly,  if  the
        variable  were  not incremented in the loop,  then  in  this
        case,  the loop would never terminate, and the program would
        never complete.   Finally, if there is only one statement to
        be executed within the loop, it does not need braces but can
        stand alone.

             Compile and run this program.

                             THE DO-WHILE LOOP

*****************
                                          /* Chapter 3 - Program 2 */
/* This is an example of a do-while loop */

main()
{
int i;

   i = 0;
   do {
      printf("The value of i is now %d\n",i);
      i = i + 1;
   } while (i < 5);
}

*****************                             
                             
                             
             A  variation  of the while loop is illustrated  in  the
        program DOWHILE.C,  which you should load and display.  This
        program  is nearly identical to the last one except that the
        loop  begins  with the reserved word  "do",  followed  by  a
        compound  statement  in  braces,   then  the  reserved  word


                                 Page 11









                        Chapter 3 - Program Control


        "while",  and  finally  an expression in  parentheses.   The
        statements in the braces are executed repeatedly as long  as
        the expression in parentheses is true.   When the expression
        in parentheses becomes false,  execution is terminated,  and
        control passes to the statements following this statement.

             Several  things  must  be pointed  out  regarding  this
        statement.  Since  the test is done at the end of the  loop,
        the  statements  in  the braces will always be  executed  at
        least once.   Secondly,  if "i" were not changed within  the
        loop,  the loop would never terminate, and hence the program
        would  never terminate.   Finally,  just like for the  while
        loop,  if  only  one statement will be executed  within  the
        loop,  no braces are required.  Compile and run this program
        to see if it does what you think it should do.

             It  should come as no surprise to you that these  loops
        can be nested.  That is, one loop can be included within the
        compound  statement of another loop,  and the nesting  level
        has no limit.

                                THE FOR LOOP

**********************************

                                          /* Chapter 3 - Program 3 */
/* This is an example of a for loop */

main()
{
int index;

   for(index = 0;index < 6;index = index + 1)
     printf("The value of the index is %d\n",index);
}



**********************************                                
                                
                                
             The  "for" loop is really nothing new,  it is simply  a
        new  way  to describe the "while" loop.   Load and edit  the
        file  named  FORLOOP.C for an example of a  program  with  a
        "for"  loop.  The  "for" loop consists of the reserved  word
        "for" followed by a rather large expression in  parentheses.
        This expression is really composed of three fields separated
        by  semi-colons.   The  first field contains the  expression
        "index  = 0" and is an initializing field.   Any expressions
        in  this field are executed prior to the first pass  through
        the loop.   There is essentially no limit as to what can  go
        here,  but  good programming practice would require it to be
        kept simple.   Several initializing statements can be placed
        in this field, separated by commas.

             The second field,  in this case containing "index < 6",
        is  the  test which is done at the beginning  of  each  loop
        through  the program.   It can be any expression which  will
        evaluate  to a true or false.   (More will be said about the
        actual value of true and false in the next chapter.)

             The expression contained in the third field is executed
        each time the loop is executed but it is not executed  until
        after  those  statements  in the main body of the  loop  are
        executed.   This field, like the first, can also be composed
        of several operations separated by commas.

             Following  the  for()  expression  is  any  single   or
        compound statement which will be executed as the body of the


                                 Page 12









                        Chapter 3 - Program Control


        loop.   A  compound  statement  is  any  group  of  valid  C
        statements enclosed in braces.   In nearly any context in C,
        a  simple statement can be replaced by a compound  statement
        that will be treated as if it were a single statement as far
        as program control goes.  Compile and run this program.

             You  may  be  wondering why there  are  two  statements
        available that do exactly the same thing because the "while"
        and  the "for" loop do exactly the same thing.  The  "while"
        is convenient to use for a loop that you don't have any idea
        how many times the loop will be executed, and the "for" loop
        is  usually used in those cases when you are doing  a  fixed
        number  of  iterations.  The "for" loop is  also  convenient
        because  it moves all of the control information for a  loop
        into one place, between the parentheses, rather than at both
        ends  of the code.  It is your choice as to which you  would
        rather use.

                              THE IF STATEMENT

*********************************

                                         /* Chapter 3 - Program 4 */
/* This is an example of the if and the if-else statements */

main()
{
int data;

   for(data = 0;data < 10;data = data + 1) {

      if (data == 2)
         printf("Data is now equal to %d\n",data);

      if (data < 5)
         printf("Data is now %d, which is less than 5\n",data);
      else
         printf("Data is now %d, which is greater than 4\n",data);

   }  /* end of for loop */
}


*********************************                              
                              
             Load  and  display the file IFELSE.C for an example  of
        our first conditional branching statement, the "if".  Notice
        first,  that there is a "for" loop with a compound statement
        as its executable part containing two "if" statements.  This
        is an example of how statements can be nested.  It should be
        clear  to  you  that each of the  "if"  statements  will  be
        executed 10 times.

             Consider the first "if" statement.   It starts with the
        keyword  "if" followed by an expression in parentheses.   If
        the expression is evaluated and found to be true, the single
        statement following the "if" is executed,  and if false, the
        following  statement  is  skipped.   Here  too,  the  single
        statement  can be replaced by a compound statement  composed
        of  several statements bounded by  braces.   The  expression
        "data  == 2" is simply asking if the value of data is  equal
        to 2,  this will be explained in detail in the next chapter.
        (Simply suffice for now that if "data = 2" were used in this
        context, it would mean a completely different thing.)

                            NOW FOR THE IF-ELSE

             The  second  "if"  is  similar to the  first  with  the
        addition  of a new reserved word,  the "else" following  the
        first  printf  statement.   This  simply says  that  if  the
        expression in the parentheses evaluates as true,  the  first
        expression  is executed,  otherwise the expression following
        the "else" is executed.   Thus,  one of the two  expressions
        will  always be executed,  whereas in the first example  the
        single expression was either executed or skipped.  Both will



                                 Page 13









                        Chapter 3 - Program Control


        find  many uses in your C programming efforts.   Compile and
        run this program to see if it does what you expect.

                           THE BREAK AND CONTINUE

**************************

                                          /* Chapter 3 - Program 5 */
main()
{
int xx;

   for(xx = 5;xx < 15;xx = xx + 1){
      if (xx == 8)
         break;
      printf("In the break loop, xx is now %d\n",xx);
   }

   for(xx = 5;xx < 15;xx = xx + 1){
      if (xx == 8)
         continue;
      printf("In the continue loop, xx is now %d\n",xx);
   }
}


**************************                           
                           
             Load  the file named BREAKCON.C for an example  of  two
        new statements.  Notice that in the first "for", there is an
        if  statement that calls a break if xx equals 8.   The break
        will  jump  out of the loop you are in and  begin  executing
        statements following the loop,  effectively terminating  the
        loop.   This  is a valuable statement when you need to  jump
        out  of  a  loop  depending on the  value  of  some  results
        calculated  in the loop.   In this case,  when xx reaches 8,
        the  loop is terminated and the last value printed  will  be
        the previous value, namely 7.

             The  next  "for" loop,  contains a  continue  statement
        which  does not cause termination of the loop but jumps  out
        of the present iteration.  When the value of xx reaches 8 in
        this case,  the program will jump to the end of the loop and
        continue  executing  the loop,  effectively eliminating  the
        printf statement during the pass through the loop when xx is
        eight.   Compile and run the program to see if it does  what
        you expect.

                            THE SWITCH STATEMENT

************************

                                         /* Chapter 3 - Program 6 */
main()
{
int truck;

   for (truck = 3;truck < 13;truck = truck + 1) {

      switch (truck) {
         case 3  : printf("The value is three\n");
                   break;
         case 4  : printf("The value is four\n");
                   break;
         case 5  :
         case 6  :
         case 7  :
         case 8  : printf("The value is between 5 and 8\n");
                   break;
         case 11 : printf("The value is eleven\n");
                   break;
         default : printf("It is one of the undefined values\n");
                   break;
      } /* end of switch */

   } /* end of for loop */
}


************************                           
                           
             Load  and  display the file SWITCH.C for an example  of
        the  biggest construct yet in the C  language,  the  switch.
        The switch is not difficult, so don't let it intimidate you.
        It  begins with the keyword "switch" followed by a  variable
        in parentheses which is the switching variable, in this case
        "truck".   As many cases as desired are then enclosed within
        a pair of braces.  The reserved word "case" is used to begin
        each  case  entered followed by the value of  the  variable,
        then a colon, and the statements to be executed.

             In  this example,  if the variable "truck" contains the
        value 3 during this pass of the switch statement, the printf
        will  cause "The value is three" to be  displayed,  and  the
        "break" statement will cause us to jump out of the switch.

             Once  an  entry  point is  found,  statements  will  be
        executed until a "break" is found or until the program drops
        through  the bottom of the switch braces.   If the  variable
        has  the value 5,  the statements will begin executing where
        "case  5  :" is found,  but the first statements  found  are
        where the case 8 statements are.  These are executed and the
        break  statement  in the "case 8" portion  will  direct  the
        execution  out the bottom of the switch.   The various  case



                                 Page 14









                        Chapter 3 - Program Control


        values can be in any order and if a value is not found,  the
        default portion of the switch will be executed.

             It should be clear that any of the above constructs can
        be  nested  within  each  other  or  placed  in  succession,
        depending on the needs of the particular programming project
        at hand.

             Compile  and  run SWITCH.C to see if it does  what  you
        expect it to after this discussion.

*********************
                                         /* Chapter 3 - Program 7 */
main()
{
int dog,cat,pig;

   goto real_start;

   some_where:
   printf("This is another line of the mess.\n");
   goto stop_it;

/* the following section is the only section with a useable goto */
   real_start:
   for(dog = 1;dog < 6;dog = dog + 1) {
      for(cat = 1;cat < 6;cat = cat + 1) {
         for(pig = 1;pig < 4;pig = pig + 1) {
            printf("Dog = %d  Cat = %d  Pig = %d\n",dog,cat,pig);
            if ((dog + cat + pig) > 8 ) goto enough;
         };
      };
   };
   enough: printf("Those are enough animals for now.\n");
/* this is the end of the section with a useable goto statement */

   printf("\nThis is the first line out of the spaghetti code.\n");
   goto there;

   where:
   printf("This is the third line of spaghetti.\n");
   goto some_where;

   there:
   printf("This is the second line of the spaghetti code.\n");
   goto where;

   stop_it:
   printf("This is the last line of this mess.\n");

}




*********************        
             Load  and display the file GOTOEX.C for an example of a
        file  with some "goto" statements in it.   To use  a  "goto"
        statement,  you simply use the reserved word "goto" followed
        by the symbolic name to which you wish to jump.  The name is
        then  placed  anywhere in the program followed by  a  colon.
        You  are  not  allowed to jump into any loop,  but  you  are
        allowed to jump out of a loop.  Also, you are not allowed to
        jump out of any function into another.   These attempts will
        be flagged by your C compiler as an error if you attempt any
        of them.

             This  particular program is really a mess but it  is  a
        good example of why software writers are trying to eliminate
        the  use of the "goto" statement as much as  possible.   The
        only place in this program where it is reasonable to use the
        "goto"  is the one in line 18 where the program jumps out of
        the three nested loops in one jump.   In this case it  would
        be  rather messy to set up a variable and jump  successively
        out of all three loops but one "goto" statement gets you out
        of all three.

             Some  persons say the "goto" statement should never  be
        used  under  any circumstances, but this  is  rather  narrow
        minded  thinking.  If there is a place where a  "goto"  will
        clearly do a neater control flow than some other  construct,
        feel free to use it.  It should not be abused however, as it
        is in the rest of the program on your monitor.

             Entire  books  are written on  "gotoless"  programming,
        better known as Structured Programming.   These will be left
        to  your  study.   One  point  of reference  is  the  Visual
        Calculater described in Chapter 14 of this  tutorial.   This
        program  is  contained in four separately compiled  programs
        and  is a rather large complex program.   If you spend  some
        time studying the source code,  you will find that there  is
        not  a single "goto" statement anywhere in it.   Compile and
        run  GOTOEX.C  and study its output.   It would  be  a  good
        exercise  to rewrite it and see how much more readable it is
        when the statements are listed in order.



                                 Page 15









                        Chapter 3 - Program Control


                       FINALLY, A MEANINGFUL PROGRAM

************************

                                       /* Chapter 3 - Program 8 */
/****************************************************************/
/*                                                              */
/*     This is a temperature conversion program written in      */
/*      the C programming language. This program generates      */
/*      and displays a table of farenheit and centigrade        */
/*      temperatures, and lists the freezing and boiling        */
/*      of water.                                               */
/*                                                              */
/****************************************************************/

main()
{
int count;        /* a loop control variable               */
int farenheit;    /* the temperature in farenheit degrees  */
int centigrade;   /* the temperature in centigrade degrees */

   printf("Centigrade to Farenheit temperature table\n\n");

   for(count = -2;count <= 12;count = count + 1){
      centigrade = 10 * count;
      farenheit = 32 + (centigrade * 9)/5;
      printf("  C =%4d   F =%4d  ",centigrade,farenheit);
      if (centigrade == 0)
         printf(" Freezing point of water");
      if (centigrade == 100)
         printf(" Boiling point of water");
      printf("\n");
   } /* end of for loop */
}



************************                       
             Load  the  file  named TEMPCONV.C for an example  of  a
        useful,  even  though somewhat limited program.   This is  a
        program  that generates a list of centigrade  and  farenheit
        temperatures  and prints a message out at the freezing point
        of water and another at the boiling point of water.

             Of particular importance is the formatting.  The header
        is  simply  several lines of comments  describing  what  the
        program  does in a manner that catches the readers attention
        and  is  still  pleasing to the  eye.  You  will  eventually
        develop your own formatting style, but this is a good way to
        start.   Also if you observe the for loop,  you will  notice
        that  all  of  the contents of the  compound  statement  are
        indented  3 spaces to the right of the "for" reserved  word,
        and  the  closing brace is lined up under the "f" in  "for".
        This  makes debugging a bit easier because the  construction
        becomes  very  obvious.   You  will  also  notice  that  the
        "printf"  statements that are in the "if" statements  within
        the  big  "for" loop are indented  three  additional  spaces
        because they are part of another construct.

             This  is  the first program in which we used more  than
        one  variable.   The three variables are simply  defined  on
        three  different lines and are used in the same manner as  a
        single variable was used in previous programs.   By defining
        them  on different lines,  we have an opportunity to  define
        each with a comment.

                      ANOTHER POOR PROGRAMMING EXAMPLE

*********************

                                        /* Chapter 3 - Program 9 */
main()
{
int x1,x2,x3;

   printf("Centigrade to Farenheit temperature table\n\n");

   for(x1 = -2;x1 <= 12;x1 = x1 + 1){
      x3 = 10 * x1;
      x2 = 32 + (x3 * 9)/5;
      printf("  C =%4d   F =%4d  ",x3,x2);
      if (x3 == 0)
         printf(" Freezing point of water");
      if (x3 == 100)
         printf(" Boiling point of water");
      printf("\n");
   }
}




*********************                     
                     
             Recalling  UGLYFORM.C from the last chapter,  you saw a
        very  poorly  formatted program.   If you load  and  display
        DUMBCONV.C you will have an example of poor formatting which
        is  much closer to what you will find in practice.  This  is
        the same program as TEMPCONV.C with the comments removed and
        the variable names changed to remove the descriptive  aspect
        of  the names.  Although this program does exactly the  same
        as  the  last  one, it is much more difficult  to  read  and
        understand.   You should begin to develop  good  programming
        practices now.

             Compile  and  run  this program to  see  that  it  does
        exactly what the last one did.








                                 Page 16









                        Chapter 3 - Program Control


        PROGRAMMING EXERCISES

        1.  Write a program that writes your name on the monitor ten
            times.   Write this program three times, once with  each
            looping method.

        2.  Write a program that counts from one to ten, prints the
            values  on  a separate line for each,  and  includes  a
            message  of  your  choice when the count  is  3  and  a
            different message when the count is 7.



                                 Page 17

TOP

C Tutorial [Chapter one + Chapter Two]
Chapter 1 - Getting Started


                          WHAT IS AN IDENTIFIER?

             Before you can do anything in any language, you must at
        least know how to name an identifier.  An identifier is used
        for  any variable, function, data definition, etc.   In  the
        programming  language C, an identifier is a  combination  of
        alphanumeric  characters,  the first being a letter  of  the
        alphabet or an underline, and the remaining being any letter
        of  the alphabet, any numeric digit, or the  underline.   In
        the  case of some compilers, a dollar sign is permitted  but
        not  as the first character of an identifier.  It should  be
        pointed out that even though a dollar sign may be  permitted
        by your C compiler, it is not used anywhere in this tutorial
        since it is not in general use by C programmers, and is  not
        even allowed by most compilers.  If you do not plan to write
        any  portable  code, you can use it at will if you  feel  it
        makes your code more readable.

             Two rules must be kept in mind when naming identifiers.

        1.   The  case  of  alphabetic  characters  is  significant.
             Using  "INDEX" for a variable is not the same as  using
             "index"  and  neither  of them is  the  same  as  using
             "InDeX"  for a variable.   All three refer to different
             variables.

        2.   As C is defined, up to 32 significant characters can be
             used  and  will  be  considered  significant  by   most
             compilers.   If  more than 32 are used,  they  will  be
             ignored by the compiler.

                         WHAT ABOUT THE UNDERLINE?

             Even  though  the  underline can be used as part  of  a
        variable  name, and adds greatly to the readability  of  the
        resulting  code,  it  seems  to  be  used  very  little   by
        experienced   C  programmers.   It  adds  greatly   to   the
        readability  of  a  program to  use  descriptive  names  for
        variables  and  it  would be to your  advantage  to  do  so.
        Pascal  programmers tend to use long descriptive names,  but
        most C programmers tend to use short cryptic names.  Most of
        the  example programs in this tutorial use very short  names
        for that reason.

             Any computer program has two entities to consider,  the
        data,  and  the program.   They are highly dependent on  one
        another  and  careful planning of both will lead to  a  well
        planned and well written program.   Unfortunately, it is not
        possible  to study either completely without a good  working
        knowledge of the other.  For this reason, this tutorial will
        jump  back  and forth between teaching  methods  of  program


                                 Page 3









                        Chapter 1 - Getting Started


        writing  and  methods of data  definition.    Simply  follow
        along and you will have a good understanding of both.   Keep
        in  mind that,  even though it seems expedient to  sometimes
        jump right into the program coding,  time spent planning the
        data  structures  will be well spent and the  final  program
        will reflect the original planning.

                        HOW THIS TUTORIAL IS WRITTEN

             As  you go through the example programs,  you will find
        that  every  program  is complete.   There  are  no  program
        fragments that could be confusing.   This allows you to  see
        every  requirement that is needed to use any of the features
        of C as they are presented.  Some tutorials I have seen give
        very few, and very complex examples.  They really serve more
        to  confuse  the  student.  This tutorial  is  the  complete
        opposite  because  it strives to cover each  new  aspect  of
        programming  in  as  simple a  context  as  possible.   This
        method,  however,  leads to a lack of knowledge in  how  the
        various  parts  are  combined.  For that  reason,  the  last
        chapter is devoted entirely to using the features taught  in
        the  earlier  chapters. It will illustrate how  to  put  the
        various features together to create a usable program.   They
        are given for your study, and are not completely  explained.
        Enough details of their operation are given to allow you  to
        understand how they work after you have completed all of the
        previous lessons.

========================================= FIRSTEX.C

main()
{
int index;

   for (index = 0;index < 7;index = index + 1)
      printf("First example program.\n");
}


==========================================

             At this point, you should load and run FIRSTEX.C if you
        have  not  yet  done  so, to see that  your  C  compiler  is
        properly  loaded and operating.  



                                 Page 4




                        Chapter 1 - Getting Started


        You will get the sourcefile displayed on the monitor by your
        editor.   

                                 Page 5


                      Chapter 2 - Getting started in C


                            YOUR FIRST C PROGRAM

=================TRIVIAL.C
main()
{
}
==================

             The best way to get started with C is to actually  look
        at  a program, so load the file named TRIVIAL.C and  display
        it on the monitor.  You are looking at the simplest possible
        C  program.  There is no way to simplify this program or  to
        leave  anything out.  Unfortunately, the program doesn't  do
        anything.

             The  word  "main" is very important,  and  must  appear
        once,  and only once in every C program.   This is the point
        where execution is begun when the program is run.   We  will
        see  later that this does not have to be the first statement
        in  the  program  but  it must exist  as  the  entry  point.
        Following  the "main" program name is a pair of  parentheses
        which  are  an  indication to the compiler that  this  is  a
        function.   We will cover exactly what a function is in  due
        time.   For now,  I suggest that you simply include the pair
        of parentheses.

             The  two curly brackets,  properly called  braces,  are
        used to define the limits of the program itself.  The actual
        program  statements  go between the two braces and  in  this
        case,  there  are  no statements because  the  program  does
        absolutely  nothing.  You can compile and run this  program,
        but since it has no executable statements, it does  nothing.
        Keep in mind however, that it is a valid C program.


                       A PROGRAM THAT DOES SOMETHING

========================================WRTSOME.C
main()
{
   printf("This is a line of text to output.");
}
========================================


             For  a much more interesting program,  load the program
        named WRTSOME.C and display it on your monitor.   It is  the
        same  as  the  previous  program  except  that  it  has  one
        executable statement between the braces.

             The  executable  statement  is a  call  to  a  function
        supplied  as a part of your C library.  Once again, we  will
        not worry about what a function is, but only how to use this
        one named "printf".  In order to output text to the monitor,
        it  is  put within the function parentheses and  bounded  by
        quotation  marks.   The  end  result  is  that  whatever  is
        included  between the quotation marks will be  displayed  on
        the monitor when the program is run.

             Notice the semi-colon at the end of the line.  C uses a
        semi-colon as a statement terminator,  so the semi-colon  is
        required  as  a  signal to the compiler that  this  line  is
        complete.   This  program  is also executable,  so  you  can
        compile  and  run  it to see if it does what  you  think  it
        should.



                                 Page 6









                      Chapter 2 - Getting started in C

================================================WRTMORE.C
main()
{
   printf("This is a line of text to output.\n");
   printf("And this is another ");
   printf("line of text.\n\n");
   printf("This is a third line.\n");
}
================================================


                      ANOTHER PROGRAM WITH MORE OUTPUT

             Load  the  program  WRTMORE.C and display  it  on  your
        monitor  for an example of more output and another small but
        important concept.  You will see that there are four program
        statements  in  this program, each one being a call  to  the
        function  "printf".   The top line will be  executed  first,
        then the next, and so on, until the fourth line is complete.
        The statements are executed in order from top to bottom.

             Notice  the funny character near the end of  the  first
        line,  namely  the backslash.   The backslash is used in the
        printf   statement  to  indicate  that  a  special   control
        character  is  following.  In this case, the  "n"  indicates
        that  a  "newline" is requested.  This is an  indication  to
        return  the cursor to the left side of the monitor and  move
        down  one  line.  It is commonly referred to as  a  carriage
        return/line  feed.  Any place within text that  you  desire,
        you  can put a newline character and start a new line.   You
        could even put it in the middle of a word and split the word
        between two lines.  The C compiler considers the combination
        of the backslash and letter n as one character.

             A complete description of this program is now possible.
        The  first  printf outputs a line of text  and  returns  the
        carriage.   The  second printf outputs a line but  does  not
        return  the carriage so that the third line is  appended  to
        the second, then followed by two carriage returns, resulting
        in a blank line.  Finally the fourth "printf" outputs a line
        followed by a carriage return and the program is complete.

             Compile and run this program to see if it does what you
        expect  it to do.   It would be a good idea at this time for
        you to experiment by adding additional lines of printout  to
        see if you understand how the statements really work.
                  

       LETS PRINT SOME NUMBERS

==========================================ONEINT.C
main()
{
int index;
   index = 13;
   printf("The value of the index is %d\n",index);
   index = 27;
   printf("The value of the index is %d\n",index);
   index = 10;
   printf("The value of the index is %d\n",index);
}
===========================================


             Load  the  file named ONEINT.C and display  it  on  the
        monitor for our first example of how to work with data in  a
        C program.  The entry point "main" should be clear to you by
        now as well as the beginning brace.  The first new thing  we
        encounter is the line containing "int index;", which is used
        to define an integer variable named "index".  The "int" is a
        reserved  word  in  C, and can therefore  not  be  used  for
        anything else.  It defines a variable that can have a  value
        from -32768 to 32767 in most C compilers for microcomputers.
        The variable name, "index", can be any name that follows the
        rules for an identifier and is not one of the reserved words
        for C.  The final character on the line, the semi-colon,  is
        the statement terminator used in C.


                                 Page 7









                      Chapter 2 - Getting started in C



             Note  that, even though we have defined a variable,  we
        have not yet assigned a value to it.  We will see in a later
        chapter  that additional integers could also be  defined  on
        the  same  line,  but we will  not  complicate  the  present
        situation.

             Observing the main body of the program, you will notice
        that  there are three statements that assign a value to  the
        variable  "index",  but only one at a time.   The first  one
        assigns the value of 13 to "index", and its value is printed
        out.   (We will see how shortly.)  Later, the value of 27 is
        assigned to "index",  and finally 10 is assigned to it, each
        value  being  printed out.   It should be intuitively  clear
        that  "index"  is  indeed  a variable  and  can  store  many
        different  values.   Please note that many times  the  words
        "printed  out" are used to mean "displayed on the  monitor".
        You  will  find that in many cases  experienced  programmers
        take  this  liberty,  probably due to the "printf"  function
        being used for monitor display.

                          HOW DO WE PRINT NUMBERS

             To  keep  our promise,  let's return  to  the  "printf"
        statements  for a definition of how they work.   Notice that
        they are all identical and that they all begin just like the
        "printf"  statements  we  have  seen  before.    The   first
        difference occurs when we come to the % character.   This is
        a  special character that signals the output routine to stop
        copying characters to the output and do something different,
        namely output a variable.   The % sign is used to signal the
        output  of  many different types of variables, but  we  will
        restrict  ourselves  to  only one  for  this  example.   The
        character  following the % sign is a "d", which signals  the
        output routine to get a decimal value and output it.   Where
        the decimal value comes from will be covered shortly.  After
        the  "d",  we  find the familiar \n, which is  a  signal  to
        return the video "carriage", and the closing quotation mark.

             All  of  the  characters between  the  quotation  marks
        define  the pattern of data to be output by this  statement,
        and  after  the pattern,  there is a comma followed  by  the
        variable name "index".  This is where the "printf" statement
        gets  the decimal value which it will output because of  the
        "%d"  we saw earlier.   We could add more "%d" output  field
        descriptors within the brackets and more variables following
        the  description  to cause more data to be printed with  one
        statement.   Keep in mind however, that it is important that
        the  number of field descriptors and the number of  variable
        definitions must be the same or the runtime system will  get
        confused and probably quit with a runtime error.


                                 Page 8







                      Chapter 2 - Getting started in C



             Much  more  will  be  covered at a later  time  on  all
        aspects  of input and output formatting.  A reasonably  good
        grasp  of  these  fundamentals are  necessary  in  order  to
        understand  the following lessons.  It is not  necessary  to
        understand everything about output formatting at this  time,
        only a fair understanding of the basics.

             Compile and run ONEINT.C and observe the output.

                        HOW DO WE ADD COMMENTS IN C


=========================================================COMMENTS.C
       /* This is a comment ignored by the compiler */

main() /* This is another comment ignored by the compiler */
{
   printf("We are looking at how comments are "); /* A comment is
                                                     allowed to be
                                                     continued on
                                                     another line */
   printf("used in C.\n");
}
     /* One more comment for effect */

===========================================================



             Load the file COMMENTS.C and observe it on your monitor
        for an example of how comments can be added to a C  program.
        Comments  are  added to make a program more readable to  you
        but the compiler must ignore the comments.   The slash  star
        combination  is used in C for comment delimiters.   They are
        illustrated  in the program at hand.   Please note that  the
        program does not illustrate good commenting practice, but is
        intended  to illustrate where comments can go in a  program.
        It is a very sloppy looking program.

             The  first slash star combination introduces the  first
        comment  and  the star slash at the end of  the  first  line
        terminates this comment.  Note that this comment is prior to
        the beginning of the program illustrating that a comment can
        precede the program itself.  Good programming practice would
        include  a  comment  prior  to  the  program  with  a  short
        introductory  description of the program.   The next comment
        is  after the "main()" program entry point and prior to  the
        opening brace for the program code itself.

             The  third  comment starts after the  first  executable
        statement and continues for four lines.   This is  perfectly
        legal  because  a comment can continue for as many lines  as
        desired  until  it is terminated.   Note carefully  that  if
        anything  were included in the blank spaces to the  left  of
        the  three  continuation lines of the comment,  it would  be
        part  of the comment and would not be  compiled.   The  last
        comment  is located following the completion of the program,
        illustrating  that  comments can go nearly anywhere in  a  C
        program.

             Experiment  with  this program by  adding  comments  in
        other places to see what will happen. Comment out one of the
        printf  statements by putting comment delimiters both before
        and after it and see that it does not get printed out.

             Comments are very important in any programming language
        because  you will soon forget what you did and why  you  did
        it.   It  will  be  much  easier to modify  or  fix  a  well


                                 Page 9







                      Chapter 2 - Getting started in C


        commented  program  a year from now than one with few or  no
        comments.   You will very quickly develop your own  personal
        style of commenting.

             Some  C  compilers will allow you  to  "nest"  comments
        which  can  be  very handy if you need to  "comment  out"  a
        section of code during debugging.  Since nested comments are
        not a part of the proposed ANSI standard, none will be  used
        in this tutorial.  Check the documentation for your compiler
        to see if they are permitted with your implementation of C.

                           GOOD FORMATTING STYLE

==================================================GOODFORM.C
main()  /* Main program starts here */
{
   printf("Good form ");
   printf          ("can aid in ");
   printf                     ("understanding a program.\n");
   printf("And bad form ");
   printf             ("can make a program ");
   printf                                ("unreadable.\n");
}

=====================================================
             Load  the  file  GOODFORM.C  and  observe  it  on  your
        monitor.   It  is  an example of a well  formatted  program.
        Even though it is very short and therefore does very little,
        it  is very easy to see at a glance what it does.   With the
        experience  you have already gained in  this  tutorial,  you
        should  be  able  to very quickly grasp the meaning  of  the
        program in it's entirety.  Your C compiler ignores all extra
        spaces  and  all carriage returns  giving  you  considerable
        freedom  concerning how you format your program.   Indenting
        and  adding spaces is entirely up to you and is a matter  of
        personal  taste.   Compile and run the program to see if  it
        does what you expect it to do.


=====================================UGLYFORM.C

main()  /* Main program starts here */{printf("Good form ");printf
("can aid in ");printf("understanding a program.\n")
;printf("And bad form ");printf("can make a program ");
printf("unreadable.\n");}

================================================

             Now load and display the program UGLYFORM.C and observe
        it.   How  long  will it take you to figure  out  what  this
        program  will do?   It doesn't matter to the compiler  which
        format style you use, but it will matter to you when you try
        to  debug  your program.   Compile this program and run  it.
        You may be surprised to find that it is the same program  as
        the  last  one,  except for the formatting.   Don't get  too
        worried about formatting style yet.  You will have plenty of
        time  to  develop  a  style of your own  as  you  learn  the
        language.   Be observant of styles as you see C programs  in
        magazines, books, and other publications.

             This  should  pretty well cover the basic  concepts  of
        programming  in  C,  but as there are many other  things  to
        learn, we will forge ahead to additional program structure.


        PROGRAMMING EXERCISES

        1. Write a program to display your name on the monitor.

        2. Modify  the  program to display your address  and  phone
           number  on  separate  lines  by  adding  two  additional
           "printf" statements.



                                 Page 10

TOP

JinTech Semiconductor Co., Ltd JinTech Semiconductor Co., Ltd - About Us JinTech Semiconductor Co., Ltd - Our Service JinTech Semiconductor Co., Ltd - Expected Quality System Certification JinTech Semiconductor Co., Ltd - Contact Us Our Partners – Sai Fung Electronics Ltd