C Tutorial

Coronado Enterprises 12501 Coronado Ave. NE Albuquerque, NM 87122

June 30, 1986

Abstract

Just a C-Tutor for IBM and compatible PCs. Because C is much device independent you can use this Tutorial for other machines, too.

Contents

1 Introduction 2

2 Getting Started 5 2.1 WHAT IS AN IDENTIFIER? . . . . . . . . . . . . . . . . . . . . . . 5 2.2 WHAT ABOUT THE UNDERLINE? . . . . . . . . . . . . . . . . . 5 2.3 HOW THIS TUTORIAL IS WRITTEN . . . . . . . . . . . . . . . . 6

3 Getting started in C 7 3.1 YOUR FIRST C PROGRAM . . . . . . . . . . . . . . . . . . . . . . 7 3.2 A PROGRAM THAT DOES SOMETHING . . . . . . . . . . . . . . 7 3.3 A PROGRAM WITH MORE OUTPUT . . . . . . . . . . . . . . . . 8 3.4 LETS PRINT SOME NUMBERS . . . . . . . . . . . . . . . . . . . . 8 3.5 HOW DO WE ADD COMMENTS IN C . . . . . . . . . . . . . . . . 9 3.6 PROGRAMMING EXERCISES . . . . . . . . . . . . . . . . . . . . 10

4 Program Control 11 4.1 THE WHILE LOOP . . . . . . . . . . . . . . . . . . . . . . . . . . . 11 4.2 THE DO-WHILE LOOP . . . . . . . . . . . . . . . . . . . . . . . . 11 4.3 THE FOR LOOP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12 4.4 THE IF STATEMENT . . . . . . . . . . . . . . . . . . . . . . . . . . 12 4.5 NOW FOR THE IF–ELSE . . . . . . . . . . . . . . . . . . . . . . . 13 4.6 THE BREAK AND CONTINUE . . . . . . . . . . . . . . . . . . . . 13 4.7 THE SWITCH STATEMENT . . . . . . . . . . . . . . . . . . . . . . 13 4.8 FINALLY, A MEANINGFUL PROGRAM . . . . . . . . . . . . . . 14 4.9 ANOTHER POOR PROGRAMMING EXAMPLE . . . . . . . . . . 15 4.10 PROGRAMMING EXERCISES . . . . . . . . . . . . . . . . . . . . 15

5 Assignment & Logical compares 16 5.1 INTEGER ASSIGNMENT STATEMENTS . . . . . . . . . . . . . . 16 5.2 ADDITIONAL DATA TYPES . . . . . . . . . . . . . . . . . . . . . 17 5.3 DATA TYPE MIXING . . . . . . . . . . . . . . . . . . . . . . . . . 17 5.4 HOW TO USE THE NEW DATA TYPES . . . . . . . . . . . . . . 17 5.5 LOTS OF VARIABLE TYPES . . . . . . . . . . . . . . . . . . . . . 18 5.6 THE CONVERSION CHARACTERS . . . . . . . . . . . . . . . . . 18 5.7 LOGICAL COMPARES . . . . . . . . . . . . . . . . . . . . . . . . . 19 5.8 MORE COMPARES . . . . . . . . . . . . . . . . . . . . . . . . . . . 19 5.9 ADDITIONAL COMPARE CONCEPTS . . . . . . . . . . . . . . . 20 5.10 LOGICAL EVALUATION . . . . . . . . . . . . . . . . . . . . . . . . 20 5.11 PRECEDENCE OF OPERATORS . . . . . . . . . . . . . . . . . . . 20 5.12 THIS IS A TRICK, BE CAREFUL . . . . . . . . . . . . . . . . . . . 21 5.13 POTENTIAL PROBLEM AREAS . . . . . . . . . . . . . . . . . . . 21 5.14 THE CRYPTIC PART OF C . . . . . . . . . . . . . . . . . . . . . . 21 5.15 THE CRYPTIC ARITHMETIC OPERATOR . . . . . . . . . . . . . 22 5.16 THE CONDITIONAL EXPRESSION . . . . . . . . . . . . . . . . . 22 5.17 TO BE CRYPTIC OR NOT TO BE CRYPTIC . . . . . . . . . . . 22 5.18 PROGRAMMING EXERCISES . . . . . . . . . . . . . . . . . . . . 23

6 Functions and variables 24 6.1 OUR FIRST USER DEFINED FUNCTION . . . . . . . . . . . . . . 24 6.2 DEFINING THE FUNCTIONS . . . . . . . . . . . . . . . . . . . . . 24 6.3 PASSING A VALUE TO A FUNCTION . . . . . . . . . . . . . . . . 25 6.4 MORE ABOUT PASSING A VALUE TO A FUNCTION . . . . . . 25 6.5 NOW TO CONFESS A LITTLE LIE . . . . . . . . . . . . . . . . . 25 6.6 FLOATING POINT FUNCTIONS . . . . . . . . . . . . . . . . . . . 26 6.7 SCOPE OF VARIABLES . . . . . . . . . . . . . . . . . . . . . . . . 27 6.8 MORE ON “AUTOMATIC” VARIABLES . . . . . . . . . . . . . . 27 6.9 USING THE SAME NAME AGAIN . . . . . . . . . . . . . . . . . . 28 6.10 WHAT IS A REGISTER VARIABLE? . . . . . . . . . . . . . . . . . 28 6.11 WHERE DO I DEFINE VARIABLES? . . . . . . . . . . . . . . . . 28 6.12 STANDARD FUNCTION LIBRARIES . . . . . . . . . . . . . . . . 28 6.13 WHAT IS RECURSION? . . . . . . . . . . . . . . . . . . . . . . . . 29 6.14 WHAT DID IT DO? . . . . . . . . . . . . . . . . . . . . . . . . . . . 29 6.15 ANOTHER EXAMPLE OF RECURSION . . . . . . . . . . . . . . . 30 6.16 PROGRAMMING EXERCISES . . . . . . . . . . . . . . . . . . . . 30

7 Defines and Macros 31 7.1 DEFINES AND MACROS . . . . . . . . . . . . . . . . . . . . . . . 31 7.2 IS THIS REALLY USEFUL? . . . . . . . . . . . . . . . . . . . . . . 31 7.3 WHAT IS A MACRO? . . . . . . . . . . . . . . . . . . . . . . . . . . 32 7.4 LETS LOOK AT A WRONG MACRO . . . . . . . . . . . . . . . . 32 7.5 PROGRAMMING EXERCISE . . . . . . . . . . . . . . . . . . . . . 32

8 Strings and Arrays 33 8.1 WHAT IS A STRING? . . . . . . . . . . . . . . . . . . . . . . . . . 33 8.2 WHAT IS AN ARRAY? . . . . . . . . . . . . . . . . . . . . . . . . . 33 8.3 HOW DO WE USE THE STRING? . . . . . . . . . . . . . . . . . . 33 8.4 OUTPUTTING PART OF A STRING . . . . . . . . . . . . . . . . . 34 8.5 SOME STRING SUBROUTINES . . . . . . . . . . . . . . . . . . . . 34 8.6 ALPHABETICAL SORTING OF STRINGS . . . . . . . . . . . . . 34 8.7 COMBINING STRINGS . . . . . . . . . . . . . . . . . . . . . . . . . 35 8.8 AN ARRAY OF INTEGERS . . . . . . . . . . . . . . . . . . . . . . 35 8.9 AN ARRAY OF FLOATING POINT DATA . . . . . . . . . . . . . 35 8.10 GETTING DATA BACK FROM A FUNCTION . . . . . . . . . . . 35 8.11 ARRAYS PASS DATA BOTH WAYS . . . . . . . . . . . . . . . . . 36 8.12 A HINT AT A FUTURE LESSON . . . . . . . . . . . . . . . . . . . 36 8.13 MULTIPLY DIMENSIONED ARRAYS . . . . . . . . . . . . . . . . 36 8.14 PROGRAMMING EXERCISES . . . . . . . . . . . . . . . . . . . . 37

9 Pointers 38 9.1 WHAT IS A POINTER? . . . . . . . . . . . . . . . . . . . . . . . . 38 9.2 TWO VERY IMPORTANT RULES . . . . . . . . . . . . . . . . . . 38 9.3 MEMORY AIDS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38 9.4 ANOTHER POINTER . . . . . . . . . . . . . . . . . . . . . . . . . 39 9.5 THERE IS ONLY ONE VARIABLE . . . . . . . . . . . . . . . . . . 39 9.6 DECLARE A POINTER . . . . . . . . . . . . . . . . . . . . . . . . 39 9.7 THE SECOND PROGRAM WITH POINTERS . . . . . . . . . . . 40 9.8 STRING VARIABLES ARE POINTERS . . . . . . . . . . . . . . . 40 9.9 POINTER ARITHMETIC . . . . . . . . . . . . . . . . . . . . . . . 41

9.9.1 INTEGER POINTER . . . . . . . . . . . . . . . . . . . . . . 41 9.10 FUNCTION DATA RETURN WITH A POINTER . . . . . . . . . . 41 9.11 POINTERS ARE VALUABLE . . . . . . . . . . . . . . . . . . . . . 42 9.12 PROGRAMMING EXERCISES . . . . . . . . . . . . . . . . . . . . 42

10 Standard Input/Output 43 10.1 THE STDIO.H HEADER FILE . . . . . . . . . . . . . . . . . . . . . 43 10.2 INPUT & OUTPUT . . . . . . . . . . . . . . . . . . . . . . . . . . . 43 10.3 OTHER INCLUDE FILES . . . . . . . . . . . . . . . . . . . . . . . 44 10.4 BACK TO THE FILE NAMED “SIMPLEIO.C” . . . . . . . . . . . 44 10.5 DOS IS HELPING US OUT . . . . . . . . . . . . . . . . . . . . . . 44 10.6 ANOTHER STRANGE I/O METHOD . . . . . . . . . . . . . . . . 45

10.6.1 LINE FEED . . . . . . . . . . . . . . . . . . . . . . . . . . . 45 10.7 WHICH METHOD IS BEST? . . . . . . . . . . . . . . . . . . . . . . 46 10.8 NOW TO READ IN SOME INTEGERS . . . . . . . . . . . . . . . . 46 10.9 IT MAKES WRONG ANSWERS SOMETIMES . . . . . . . . . . . 47 10.10CHARACTER STRING INPUT . . . . . . . . . . . . . . . . . . . . 47 10.11INPUT/OUTPUT PROGRAMMIG . . . . . . . . . . . . . . . . . . 48 10.12IN MEMORY I/O . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48 10.13STANDARD ERROR OUTPUT . . . . . . . . . . . . . . . . . . . . 49 10.14THE exit() STATEMENT . . . . . . . . . . . . . . . . . . . . . . . . 50 10.15PROGRAMMING EXERCISE . . . . . . . . . . . . . . . . . . . . . 50

11 File Input/Output 51 11.1 OUTPUT TO A FILE . . . . . . . . . . . . . . . . . . . . . . . . . . 51 11.2 OPENING A FILE . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51

11.2.1 READING (“r”) . . . . . . . . . . . . . . . . . . . . . . . . . 51 11.2.2 WRITING (“w”) . . . . . . . . . . . . . . . . . . . . . . . . . 51 11.2.3 APPENDING (“a”) . . . . . . . . . . . . . . . . . . . . . . . 52

11.3 OUTPUTTING TO THE FILE . . . . . . . . . . . . . . . . . . . . . 52 11.4 CLOSING A FILE . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52 11.5 SINGLE CHARACTER OUTPUT . . . . . . . . . . . . . . . . . . . 52 11.6 THE “putc” FUNCTION . . . . . . . . . . . . . . . . . . . . . . . . 53 11.7 READING A FILE . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53 11.8 READING A WORD . . . . . . . . . . . . . . . . . . . . . . . . . . 54 11.9 READING A FULL LINE . . . . . . . . . . . . . . . . . . . . . . . . 54 11.10HOW TO USE A VARIABLE FILENAME . . . . . . . . . . . . . . 54 11.11HOW DO WE PRINT? . . . . . . . . . . . . . . . . . . . . . . . . . 55 11.12PROGRAMMING EXERCISES . . . . . . . . . . . . . . . . . . . . 55

12 Structures and Unions 56

12.1 WHAT IS A STRUCTURE? . . . . . . . . . . . . . . . . . . . . . . 56 12.1.1 A SINGLE COMPOUND VARIABLE . . . . . . . . . . . . . 56 12.1.2 HOW DO WE USE THE RESULTING DATA? . . . . . . . 57

12.2 AN ARRAY OF STRUCTURES . . . . . . . . . . . . . . . . . . . . 57 12.3 USING POINTERS AND STRUCTURES TOGETHER . . . . . . . 58 12.4 POINTER ARITHMETIC . . . . . . . . . . . . . . . . . . . . . . . 58 12.5 NESTED AND NAMED STRUCTURES . . . . . . . . . . . . . . . 59 12.6 NOW TO USE SOME OF THE FIELDS . . . . . . . . . . . . . . . 59 12.7 MORE ABOUT STRUCTURES . . . . . . . . . . . . . . . . . . . . 60 12.8 WHAT ARE UNIONS? . . . . . . . . . . . . . . . . . . . . . . . . . 60 12.9 THE TYPEDEF-CONCEPT . . . . . . . . . . . . . . . . . . . . . . 61 12.10SUMMARY . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62 12.11PROGRAMMING EXERCISES . . . . . . . . . . . . . . . . . . . . 62

13 Dynamic Allocation 63 13.1 DYNAMIC ALLOCATION . . . . . . . . . . . . . . . . . . . . . . . 63 13.2 DYNAMIC VARIABLE CREATION . . . . . . . . . . . . . . . . . . 63

13.2.1 WHAT IS A HEAP? . . . . . . . . . . . . . . . . . . . . . . . 63 13.2.2 MORE ABOUT SEGMENTS . . . . . . . . . . . . . . . . . . 64 13.2.3 THE “MALLOC” FUNCTION . . . . . . . . . . . . . . . . . 64

13.3 USING THE DYNAMICALLY ALLOCATED MEMORY BLOCK . 65 13.4 GETTING RID OF THE DYNAMICALLY ALLOCATED DATA . 65 13.5 AN ARRAY OF POINTERS . . . . . . . . . . . . . . . . . . . . . . 66 13.6 A LINKED LIST . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 66

13.6.1 THE DATA DEFINITIONS . . . . . . . . . . . . . . . . . . . 67 13.6.2 THE FIRST FIELD . . . . . . . . . . . . . . . . . . . . . . . 67 13.6.3 FILLING ADDITIONAL STRUCTURES . . . . . . . . . . . 67 13.6.4 PRINTING THE DATA OUT . . . . . . . . . . . . . . . . . 68

13.7 SOMETHING MORE . . . . . . . . . . . . . . . . . . . . . . . . . . 69 13.8 ANOTHER NEW FUNCTION - CALLOC . . . . . . . . . . . . . . 69 13.9 PROGRAMMING EXERCISES . . . . . . . . . . . . . . . . . . . . 69

14 Character and Bit Manipulation 70 14.1 UPPER AND LOWER CASE . . . . . . . . . . . . . . . . . . . . . 70 14.2 CLASSIFICATION OF CHARACTERS . . . . . . . . . . . . . . . . 70 14.3 THE LOGICAL FUNCTIONS . . . . . . . . . . . . . . . . . . . . . 71 14.4 THE SHIFT INSTRUCTIONS . . . . . . . . . . . . . . . . . . . . . 71

15 Example Programs 72 15.1 WHY THIS CHAPTER? . . . . . . . . . . . . . . . . . . . . . . . . 72 15.2 DOSEX.C - The DOS Example Program . . . . . . . . . . . . . . . . 72 15.3 WHATNEXT.C - The Batch File Interrogator . . . . . . . . . . . . . 73 15.4 LIST.C - The Program Lister . . . . . . . . . . . . . . . . . . . . . . 74 15.5 VC.C - The Visual Calculator . . . . . . . . . . . . . . . . . . . . . . 74

Chapter 1

Introduction to the C Tutorial

The programming language C, was originally developed by Dennis Ritchie of Bell Laboratories and was designed to run on a PDP-11 with a UNIX operating system. Although it was originally intended to run under UNIX, there has been a great interest in running it under the MS-DOS operating system and specifically on the IBM PC and compatibles. It is an excellent language for this environment because of the simplicity of expression, the compactness of the code, and the wide range of applicability.

It is not a good “beginning” language because it is somewhat cryptic in nature. It allows the programmer a wide range of operations from high level down to a very low level approaching the level of assembly language. There seems to be no limit to the flexibility available. One experienced C programmer made the statement, “You can program anything in C”, and the statement is well supported by my own experience with the language. Along with the resulting freedom however, you take on a great deal of responsibility because it is very easy to write a program that destroys itself due to the silly little errors that the Pascal compiler will flag and call a fatal error. In C, you are very much on your own as you will soon find.

Since C is not a beginners language, I will assume you are not a beginning programmer, and I will not attempt to bore you by defining a constant and a variable. You will be expected to know these basic concepts. You will, however, be expected to know nothing of the C programming language. I will begin with the most basic concepts of C and take you up to the highest level of C programming including the usually intimidating concepts of pointers, structures, and dynamic allocation. To fully understand these concepts, it will take a good bit of time and work on your part because they not particularly easy to grasp, but they are very powerful tools. Enough said about that, you will see their power when we get there, just don’t allow yourself to worry about them yet.

Programming in C is a tremendous asset in those areas where you may want to use Assembly Language but would rather keep it a simple to write and easy to maintain program. It has been said that a program written in C will pay a premium of a 50 to 100% increase in runtime because no language is as compact or fast as Assembly Language. However, the time saved in coding can be tremendous, making it the most desirable language for many programming chores. In addition, since most programs spend 90 percent of their operating time in only 10 percent or less of the code, it is possible to write a program in C, then rewrite a small portion of the code in Assembly Language and approach the execution speed of the same program if it were written entirely in Assembly Language.

Approximately 75 percent of all new commercial programs introduced for the IBM PC have been written in C, and the percentage is probably growing. Microsoft recently introduced a new Macro Assembler, version 4.0, and they said that it was written in C. There are probably a few routines coded in Assembly Language, but the majority was written in C.

Since C was designed essentially by one person, and not by a committee, it is a very usable language but not too well defined. There is no standard for the C language, but the American National Standards Association (ANSI) is developing a standard for the language at which time it will follow rigid rules. It is interesting to note, however, that even though it does not have a standard, the differences between implementations are very small. This is probably due to the fact that the original unofficial definition was so well thought out and carefully planned that extensions to the language are not needed. Pascal, which has a rigorous definition, has many extensions by compiler writers and every extension is different. This leads to a real problem when transporting a Pascal program from one computer to another.

Even though the C language enjoys a good record when programs are transported from one implementation to another, there are differences in compilers as you will find anytime you try to use another compiler. Most of the differences become apparent when you use nonstandard extensions such as calls to the DOS BIOS, but even these differences can be minimized by careful choice of programming means.

Your first problem will not be how to program in C, but how to use your particular compiler. Since there are over 20 good compilers available, there is no way I can cover the operation of all compilers. Notes about a few of the better known compilers are given in the “COMPILER.DOC” file on the distribution diskette. Read the documentation that came with your compiler to learn how to compile and run a program.

One last note about compilers. I wrote a moderately large program in C that was composed of about 1200 lines of source code contained in 4 separately compiled files. I was initially using a very inexpensive compiler from MIX Software of Richardson, Texas that sells for $39.95. This compiler did everything I ever asked it to do and did it well, including floating point numbers. In addition, the compile times were extremely short and there were many extensions to the basic language as defined by Kernigan & Ritchie. In short, the compiler was a good implementation. Later, I switched over to a Lattice C compiler that sells for $500.00. It took a bit of work because the Lattice compiler did not have as many extensions as the MIX compiler. The Lattice compiler also took considerably longer to compile, probably 2 to 3 times as long. The big difference in the two compilers was in the execution time of the program which read in a file, did a lot of searching in memory, and displayed the results on the monitor. The final MIX program took 95 seconds to complete all operations, and the Lattice compiled program took only 10 seconds to complete. I should add that the MIX compiler has a speedup utility that increases the speed by a factor of about 8, according to one independent review, getting the speed of the MIX program in the range of the Lattice program. (I did not try the speedup program on this particular file.) The MIX compiler missed several subtle type errors during compile that were flagged as warnings by the Lattice compiler. Due to the nature of that particular program, either run-time would be acceptable and therefore either compiler would be acceptable.

The above paragraph was given only to aid you in selecting a compiler. The Lattice compiler is very difficult to use for an inexperienced programmer, but has very few limitations. The MIX compiler, on the other hand, was very easy to set up and begin using, and would be very appropriate for most “hobby” computing. Depending on your application, the most expensive is probably not the best. In this case, the MIX compiler would be great for learning the language and for many

7

applications.

Getting Started

2.1 WHAT IS AN IDENTIFIER?

Before you can do anything in any language, you must at least know how you 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. 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 eight significant characters can be used and will be considered significant. If more than eight are used, they may be ignored by the compiler. This may or may not be true of your compiler. You should check your reference manual to find out how many characters are significant for your compiler.

It should be pointed out that some C compilers allow use of a dollar sign in an identifier name, but since it is not universal, it will not be used anywhere in this tutorial. Check your documentation to see if it is permissible for your particular compiler.

2.2 WHAT ABOUT THE UNDERLINE?

Even though the underline can be used as part of a variable name, 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 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.

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

Getting started in C

3.1 YOUR FIRST C PROGRAM

The best way to get started with C is to actually look at a program, so load the file named “TRIVIAL.C” into your editor 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.

3.2 A PROGRAM THAT DOES SOMETHING

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 another function. Once again, we will not worry about what a function is, but only how to use this one. 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.

3.3 A 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 printf function statement. 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 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 the third line is appended to that of 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.

3.4 LETS PRINT SOME NUMBERS

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 on most microcomputer implementations of C. Consult your users manual for the exact definition for your compiler. 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. Consult your manual for an exact definition of an identifier for your compiler. The final character on the line, the semi–colon, is the statement terminator used in C.

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

Much more will be covered at a later time on all aspects of input and output formatting. A reasonably good grasp of this topic is 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.

3.5 HOW DO WE ADD COMMENTS IN C

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 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 compilers allow you to nest comments which can be very handy if you need to comment out a section of code during debugging. Check your compiler documentation for the availability of this feature with you particular compiler. Compile and run COMMENTS.C at this time. sectionGOOD FORMATTING STYLE 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.

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.

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

Program Control

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

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.

4.2 THE DO-WHILE LOOP

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

4.3 THE FOR LOOP

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

4.4 THE IF STATEMENT

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

4.5 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 find many uses in your C programming efforts. Compile and run this program to see if it does what you expect.

4.6 THE BREAK AND CONTINUE

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.

4.7 THE SWITCH STATEMENT

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

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

4.8 FINALLY, A MEANINGFUL PROGRAM

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.

4.9 ANOTHER POOR PROGRAMMING EX-AMPLE

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

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

Assignment & Logical compares

5.1 INTEGER ASSIGNMENT STATEMENTS

Load the file INTASIGN.C 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 four 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 next two expressions 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.

The program has no output so compiling and executing this program will be very uninteresting. Since you have 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 any C program. If you try to define a new variable after executing some statements, the compiler will issue an error.

5.2 ADDITIONAL DATA TYPES

Loading and editing MORTYPES.C 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 values between zero and 255, 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 most microcomputers.

5.3 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 required to store it. The float data type has a decimal point associated with it and, on most computers, has an allowable range of from 10 ^{−38} to 10 ^{+38}. Not all compilers have the same available range, so check your reference manual for the limits on your compiler.

5.4 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 zero to 255. In the second line therefore, when attempting to set x (a char) to −27, you may or may not get a well defined answer, it depends on your particular implementation of C.

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.

5.5 LOTS OF VARIABLE TYPES

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

Observe the file. First we define a simple int, followed by a long int and a short int. Consult your reference manual for an exact definition of these for your compiler, because they are not consistent from implementation to implementation. 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 on most microcomputers. It 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 usually 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. Consult your reference manual for the range and accuracy of the double.

Another diversion is in order at this point. Most compilers have no provisions for floating point math, but only double floating point math. They 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. 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. Your compiler may require a different number of bytes than 4 or 8. Consult your reference manual for the correct number of bytes used by your compiler.

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

5.6 THE CONVERSION CHARACTERS

Following is a list of the conversion characters and the way they are usedin the printf statement.

d decimal notation

o octal notation

x hexadecimal notation

u unsigned notation

c character notation

s string notation

f floating point notation

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. 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 any of the fields anyway you desire.

5.7 LOGICAL COMPARES

Load and view the file named COMPARES.C 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 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. 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.

5.8 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, probably 1. 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, 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 different compilers may assign a different value as long as it is non–zero.

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

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

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

5.11 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, which your compiler will define completely, 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.

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

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

5.14 THE CRYPTIC PART OF C

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

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.

5.15 THE CRYPTIC ARITHMETIC OPERA-TOR

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.

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

5.17 TO BE CRYPTIC OR NOT TO BE CRYP-TIC

Several students of C have stated that they didn’t like these three cryptic constructs and that they would simply never use them. This would 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.

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

Functions and variables

6.1 OUR FIRST USER DEFINED FUNCTION

Load and examine the file SUMSQRES.C 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. 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.

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

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

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.

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

6.5 NOW TO CONFESS A LITTLE LIE

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

6.6 FLOATING POINT FUNCTIONS

Load the program FLOATSQ.C 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 em int, but of some other type, in this case em float. This tells the compiler that when a value is returned from either of these two functions, it will be of type float.

Now refer to the function em sqr near the center of the listing and you will see that the function name is preceded by the name em float. This is an indication to the compiler that this function will return a value of type em 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 em float.

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 will be times that you will have a need for a function to return a pointer as a result of some calculation. There is a way to define a function so that it does just that. We haven’t studied pointers yet, but we will soon. This is just a short preview of things to come.

6.7 SCOPE OF VARIABLES

Load the next program, SCOPE.C, and display it for a discussion of the scope of variables in a program.

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

6.8 MORE ON “AUTOMATIC” VARIABLES

Observe the function named head1 . It 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 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. /sectionWHAT 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 variables in other separately compiled files, and that is true. Examples of this usage will be given in chapter 14 of this tutorial.

6.9 USING THE SAME NAME AGAIN

Refer to the function named head2 . It contains another definition of the variable named em 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.

6.10 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. Depending on the computer and the compiler, a small number of register variables may be allowed and are designated by putting the word register in front of the desired variable. Check your compiler documentation for the availability of this feature and the number of register variables. Most compilers that do not have any register variables available, will simply ignore the word register and run normally, keeping all variables in memory.

Register variables are only available for use with integer and character type variables. This may or may not include some of the other integer-like variables such as unsigned, long, or short. Check the documentation for your compiler.

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

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

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 the unique aspects of your compiler on your own. Many of these kinds of functions are used in the example programs in chapter 15.

6.13 WHAT IS RECURSION?

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, 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 it 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 will 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.

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

6.15 ANOTHER EXAMPLE OF RECURSION

The program named BACKWARD.C 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 backward 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.

Don’t worry about the character array defined in line 3 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.

One additional feature is built into this program. If you observe the two calls to the function, and the function itself, you will see that the function name is spelled three different ways in the last few characters. The compiler doesn’t care how they are spelled because it only uses the first 8 characters of the function name so as far as it is concerned, the function is named forward . The remaining characters are simply ignored. If your compiler uses more that 8 characters as being significant, you will need to change two of the names so that all three names are identical.

Compile and run this program and observe the results.

6.16 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 compiler will allow it.

Defines and Macros

7.1 DEFINES AND MACROS ARE AIDS TO CLEAR PROGRAMMING

Load and display the file named DEFINE.C for your first look at some defines and macros. Notice the first four lines 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. 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 the START, it would be a completely different matter. If you wanted to change all of the STARTs 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 END-ING 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.

7.2 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. 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. End–of–line is another indicator that is not universal. This will make more sense when we get to the chapters on input and output.

7.3 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 the third line 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 12 of the program is reached, index will be substituted for every A, and count will be substituted for every B. 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.

7.4 LETS LOOK AT A WRONG MACRO

Load the file named MACRO.C and display it on your screen for a better look at a macro and its use. The first 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 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.

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

7.5 PROGRAMMING EXERCISE

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.

Strings and Arrays

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

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

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.

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

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

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

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

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

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

8.8 AN ARRAY OF INTEGERS

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

8.9 AN ARRAY OF FLOATING POINT DATA

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

8.10 GETTING DATA BACK FROM A FUNC-TION

Back in chapter 6 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 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. The following line, prior to the bracket which starts the program, 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.

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

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

8.13 MULTIPLY DIMENSIONED ARRAYS

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

8.14 PROGRAMMING EXERCISES

1. Write a program with three short strings, about 6 characters each, and use strcpy to copy em 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 = array2[index],arrays[index]);

Pointers

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

9.2 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 six 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 nine 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.

9.3 MEMORY AIDS

1. Think of & as an address.

2. Think of * as a star referring to stored.

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

9.4 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 seven, 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 seven 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.

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

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.

9.6 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 9 changes the one variable which is displayed three times.

9.7 THE SECOND PROGRAM WITH POINT-ERS

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.

9.8 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 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 12, 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 characters 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 value of the eleventh element of “strg” by the statement in line 16 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 paragraph to be done properly. The third and fourth outputs will be the same, namely the letter “c”.

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

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

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

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

9.12 PROGRAMMING EXERCISES

1. Define a character array and use strcpy to copy a string into it. Print the string out by using a loop with a pointer to print out one character at a time. Initialize the pointer to the first element and use the double plus sign to increment the pointer. Use a separate integer variable to count the characters to print.

2. Modify the program to print out the string backwards by pointing to the end and using a decrementing pointer.

Standard Input/Output

10.1 THE STDIO.H HEADER FILE

Load the file SIMPLEIO.C for our first look at a file with standard I/O. Standard I/O refers to the most usual places where data is either read from, the keyboard, or written to, the video monitor. Since they are used so much, they are used as the default I/O devices and do not need to be named in the Input/Output instructions. This will make more sense when we actually start to use them so lets look at the file in front of you.

The first thing you will notice is the first line of the file, the #include “stdio.h” line. This is very much like the #define we have already studied, except that instead of a simple substitution, an entire file is read in at this point. The system will find the file named “stdio.h” and read its entire contents in, replacing this statement. Obviously then, the file named “stdio.h” must contain valid C source statements that can be compiled as part of a program. This particular file is composed of several standard #defines to define some of the standard I/O operations. The file is called a header file and you will find several different header files on the source disks that came with your compiler. Each of the header files has a specific purpose and any or all of them can be included in any program.

Most C compilers use the double quote marks to indicate that the include file will be found in the current directory. A few use the “less than” and “greater than” (< & >) signs to indicate that the file will be found in a standard header file. Nearly all MSDOS C compilers use the double quotes, and most require the include file to be in the default directory. All of the programs in this tutorial have the double quotes in the include statements. If your compiler uses the other notation, you will have to change them before compiling.

10.2 INPUT/OUTPUT OPERATIONS IN C

Actually the C programming language has no input or output operations defined as part of the language, they must be user defined. Since everybody does not want to reinvent his own input and output operations, the compiler writers have done a lot of this for us and supplied us with several input functions and several output functions to aid in our program development. The functions have become a standard, and you will find the same functions available in nearly every compiler. In fact, the industry standard of the C language definition has become the book written by Kernigan and Ritchie, and they have included these functions in their definition. You will often, when reading literature about C, find a reference to K & R. This refers to the book written by Kernigan and Ritchie. You would be advised to purchase a copy for reference.

You should print out the file named “stdio.h” and spend some time studying it. There will be a lot that you will not understand about it, but parts of it will look familiar. The name “stdio.h” is sort of cryptic for “standard input/output header”, because that is exactly what it does. It defines the standard input and output functions in the form of #defines and macros. Don’t worry too much about the details of this now. You can always return to this topic later for more study if it interests you, but you will really have no need to completely understand the “stdio.h” file. You will have a tremendous need to use it however, so these comments on its use and purpose are necessary.

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

10.4 BACK TO THE FILE NAMED “SIMPLEIO.C”

Lets continue our tour of the file in question. The one variable c is defined and a message is printed out with the familiar printf function. We then find ourselves in a continuous loop as long as c is not equal to capital X. If there is any question in your mind about the loop control, you should review chapter 3 before continuing. The two new functions within the loop are of paramount interest in this program since they are the new functions. These are functions to read a character from the keyboard and display it on the monitor one character at a time.

The function getchar() reads a single character from the standard input device, the keyboard being assumed because that is the standard input device, and assigns it to the variable c. The next function putchar(c), uses the standard output device, the video monitor, and outputs the character contained in the variable c. The character is 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.

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

We need to understand a little bit about how DOS works to understand what is happening here. When data is read from the keyboard, under DOS control, the characters are stored in a buffer until a carriage return is entered at which time the entire string of characters is given to the program. When the characters are being typed, however, the characters are displayed one at a time on the monitor. This is called echo, and happens in many of the applications you run.

With the above paragraph in mind, it should be clear that when you are typing a line of data into “SIMPLEIO”, the characters are being echoed by DOS, and when you return the carriage, the characters are given to the program. As each character is given to the program, it displays it on the screen resulting in a repeat of the line typed in. To better illustrate this, type a line with a capital X somewhere in the middle of the line. You can type as many characters as you like following the X and they will all display because the characters are being read in under DOS, echoed to the monitor, and placed in the DOS input buffer. DOS doesn’t think there is anything special about a capital X. When the string is given to the program, however, the characters are accepted by the program one at a time and sent to the monitor one at a time, until a capital X is encountered. After the capital X is displayed, the loop is terminated, and the program is terminated. The characters on the input line following the capital X are not displayed because the capital X signalled program termination.

Compile and run SIMPLEIO.C. After running the program several times and feeling confidant that you understand the above explanation, we will go on to another program.

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.

10.6 ANOTHER STRANGE I/O METHOD

Load the file named SINGLEIO.C and display it on your monitor for another method of character I/O. Once again, we start with the standard I/O header file, we define a variable named c, and we print a welcoming message. Like the last program, we are in a loop that will continue to execute until we type a capital X, but the action is a little different here.

The getch() is a new function that is a get character function. It differs from getchar() in that it does not get tied up in DOS. It reads the character in without echo, and puts it directly into the program where it is operated on immediately. This function then reads a character, immediately displays it on the screen, and continues the operation until a capital X is typed.

When you compile and run this program, you will find that there is no repeat of the lines when you hit a carriage return, and when you hit the capital X, the program terminates immediately. No carriage return is needed to get it to accept the line with the X in it. We do have another problem here, there is no linefeed with the carriage return.

10.6.1 NOW WE NEED A LINE FEED

It is not apparent to you in most application programs but when you hit the enter key, the program supplies a linefeed to go with the carriage return. You need to return to the left side of the monitor and you also need to drop down a line. The linefeed is not automatic. We need to improve our program to do this also. If you will load and display the program named BETTERIN.C, you will find a change to incorporate this feature.

In BETTERIN.C, we have two additional statements at the beginning that will define the character codes for the linefeed (LF), and the carriage return (CR). If you look at any ASCII table you will find that the codes 10 and 13 are exactly as defined here. In the main program, after outputting the character, we compare it to CR, and if it is equal to CR, we also output a linefeed which is the LF. We could have just as well have left out the two #define statements and used “if (c == 13) putchar(10);” but it would not be very descriptive of what we are doing here. The method used in the program represents better programming practice.

Compile and run BETTERIN.C to see if it does what we have said it should do. It should display exactly what you type in, including a linefeed with each carriage return, and should stop immediately when you type a capital X.

If you are using a nonstandard compiler, it may not find a “CR” because your system returns a “LF” character to indicate end-of-line. It will be up to you to determine what method your compiler uses. The quickest way is to add a printf statement that prints the input character in decimal format.

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

10.8 NOW TO READ IN SOME INTEGERS

Load and display the file named INTIN.C for an example of reading in some formatted data. The structure of this program is very similar to the last three except that we define an int type variable and loop until the variable somehow acquires the value of 100.

Instead of reading in a character at a time, as we have in the last three files, we read in an entire integer value with one call using the function named scanf . This function is very similar to the printf that you have been using for quite some time by now except that it is used for input instead of output. Examine the line with the scanf and you will notice that it does not ask for the variable “valin” directly, but gives the address of the variable since it expects to have a value returned from the function. Recall that a function must have the address of a variable in order to return the value to the calling program. Failing to supply a pointer in the scanf function is probably the most common problem encountered in using this function.

The function scanf scans the input line until it finds the first data field. It ignores leading blanks and in this case, it reads integer characters until it finds a blank or an invalid decimal character, at which time it stops reading and returns the value.

Remembering our discussion above about the way the DOS input buffer works, it should be clear that nothing is actually acted on until a complete line is entered and it is terminated by a carriage return. At this time, the buffer is input, and our program will search across the line reading all integer values it can find until the line is completely scanned. This is because we are in a loop and we tell it to find a value, print it, find another, print it, etc. If you enter several values on one line, it will read each one in succession and display the values. Entering the value of 100 will cause the program to terminate, and entering the value 100 with other values following, will cause termination before the following values are considered.

10.9 IT MAKES WRONG ANSWERS SOME-TIMES

If you enter a number up to and including 32767, it will display correctly, but if you enter a larger number, it will appear to make an error. For example, if you enter the value 32768, it will display the value of −32768, entering the value 65536 will display as a zero. These are not errors but are caused by the way an integer is defined. The most significant bit of the 16 bit pattern available for the integer variable is the sign bit, so there are only 15 bits left for the value. The variable can therefore only have the values from −32768 to 32767, any other values are outside the range of integer variables. This is up to you to take care of in your programs. It is another example of the increased responsibility you must assume using C rather than a higher level language such as Pascal, Modula-2, etc.

The above paragraph is true for most MS-DOS C compilers. There is a very small possibility that your compiler uses an integer value other than 16 bits. If that is the case, the same principles will be true but at different limits than those given above.

Compile and run this program, entering several numbers on a line to see the results, and with varying numbers of blanks between the numbers. Try entering numbers that are too big to see what happens, and finally enter some invalid characters to see what the system does with nondecimal characters.

10.10 CHARACTER STRING INPUT

Load and display the file named STRINGIN.C for an example of reading a string variable. This program is identical to the last one except that instead of an integer variable, we have defined a string variable with an upper limit of 24 characters (remember that a string variable must have a null character at the end). The variable in the scanf does not need an & because “big” is an array variable and by definition it is already a pointer. This program should require no additional explanation. Compile and run it to see if it works the way you expect.

You probably got a surprise when you ran it because it separated your sentence into separate words. When used in the string mode of input, scanf reads characters into the string until it comes to either the end of a line or a blank character. Therefore, it reads a word, finds the blank following it, and displays the result. Since we are in a loop, this program continues to read words until it exhausts the DOS input buffer. We have written this program to stop whenever it finds a capital X in column 1, but since the sentence is split up into individual words, it will stop anytime a word begins with capital X. Try entering a 5 word sentence with a capital X as the first character in the third word. You should get the first three words displayed, and the last two simply ignored when the program stops.

Try entering more than 24 characters to see what the program does. It should generate an error, but that will be highly dependent on the system you are using. In an actual program, it is your responsibility to count characters and stop when the input buffer is full. You may be getting the feeling that a lot of responsibility is placed on you when writing in C. It is, but you also get a lot of flexibility in the bargain too.

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

10.12 IN MEMORY I/O

The next operation may seem a little strange at first, but you will probably see lots of uses for it as you gain experience. Load the file named INMEM.C and display it for another type of I/O, one that never accesses the outside world, but stays in the computer. In INMEM.C, we define a few variables, then assign some values to the ones named “numbers” for illustrative purposes and then use a sprintf function. The function acts just like a normal printf function except that instead of printing the line of output to a device, it prints the line of formatted output to a character string in memory. In this case the string goes to the string variable “line”, because that is the string name we inserted as the first argument in the sprintf function. The spaces after the 2nd %d were put there to illustrate that the next function will search properly across the line. We print the resulting string and find that the output is identical to what it would have been by using a printf instead of the sprintf in the first place. You will see that when you compile and run the program shortly.

Since the generated string is still in memory, we can now read it with the function sscanf . We tell the function in its first argument that “line” is the string to use for its input, and the remaining parts of the line are exactly what we would use if we were going to use the scanf function and read data from outside the computer. Note that it is essential that we use pointers to the data because we want to return data from a function. Just to illustrate that there are many ways to declare a pointer several methods are used, but all are pointers. The first two simply declare the address of the elements of the array, while the last three use the fact that “result”, without the accompanying subscript, is a pointer. Just to keep it interesting, the values are read back in reverse order. Finally the values are displayed on the monitor.

IS THAT REALLY USE-

FUL?

It seems sort of silly to read input data from within the computer but it does have a real purpose. It is possible to read data in using any of the standard functions and then do a format conversion in memory. You could read in a line of data, look at a few significant characters, then use these formatted input routines to reduce the line of data to internal representation. That would sure beat writing your own data formatting routines.

10.13 STANDARD ERROR OUTPUT

Sometimes it is desirable to redirect the output from the standard output device to a file. However, you may still want the error messages to go to the standard output device, in our case the monitor. This next function allows you to do that. Load and display SPECIAL.C for an example of this new function.

The program consists of a loop with two messages output, one to the standard output device and the other to the standard error device. The message to the standard error device is output with the function fprintf and includes the device name stderr as the first argument. Other than those two small changes, it is the same as our standard printf function. (You will see more of the fprintf function in the next chapter, but its operation fit in better as a part of this chapter.) Ignore the line with the exit for the moment, we will return to it.

Compile and run this program, and you will find 12 lines of output on the monitor. To see the difference, run the program again with redirected output to a file named “STUFF” by entering the following line at the Dos prompt;

A> special >stuff

More information about I/O redirection can be found in your DOS manual. This time you will only get the 6 lines output to the standard error device, and if you look in your directory, you will find the file named “STUFF” containing the other 6 lines, those to the standard output device. You can use I/O redirection with any of the programs we have run so far, and as you may guess, you can also read from a file using I/O redirection but we will study a better way to read from a file in the next chapter.

10.14 WHAT ABOUT THE exit(4) STATEMENT?

Now to keep our promise about the exit(4) statement. Redisplay the file named SPECIAL.C on your monitor. The last statement simply exits the program and returns the value of 4 to DOS. Any number from 0 to 9 can be used in the parentheses for DOS communication. If you are operating in a BATCH file, this number can be tested with the ERRORLEVEL command.

Most compilers that operate in several passes return a 1 with this mechanism to indicate that a fatal error has occurred and it would be a waste of time to go on to another pass resulting in even more errors.

It is therefore wise to use a batch file for compiling programs and testing the returned value for errors. A check of the documentation for my COMPAQ, resulted in a minimal and confusing documentation of the errorlevel command, so a brief description of it is given in this file.

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

File Input/Output

11.1 OUTPUT TO A FILE

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.

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

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

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

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

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

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

11.5 OUTPUTTING A SINGLE CHARACTER AT A TIME

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.

11.6 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 again, do not erase this file because we are still not finished with it.

11.7 READING A FILE

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

TION

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 could use a char variable for this purpose. There is a problem with that however, because on some, if not most, implementations of C, the EOF returns a minus one which a char type variable is not capable of containing. A char type variable can only have the values of zero to 255, so it will return a 255 for a minus one on those compilers that use a minus one for EOF. This is a very frustrating problem to try to find because no diagnostic is given. The program simply can never find the EOF and will therefore never terminate the loop. This is easy to prevent, always use an int type variable for use in returning an EOF. You can tell what your compiler uses for EOF by looking at the “stdio.h” file where EOF is defined. That is the standard place to define such values.

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

11.8 READING A WORD AT A TIME

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 prograh(8;”�&xt¡¿

���� �� ��������� ���� �� ������� ����� ��� �� ��� ���� ����� ������� �� ��� �����

���� ������� ��� ���� ������� �� �� ����� �� ��� ����� ���������� �� ��������� ���� ����� ��� ��� ������ �� ������� ��� 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, READ-TEXT.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.

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.

11.9 FINALLY, WE READ A FULL LINE

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 for the addition of a new quantity, the NULL.

We are using fgets which reads in an entire line, including the newline character into a buffer. The buffer 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.

11.10 HOW TO USE A VARIABLE FILENAME

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.

11.11 HOW DO WE PRINT?

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. There are no definite standards as far as the name or names to be used for the printer, but some of the usual names are, “PRN”, “LPT”, “LPT1”, and “LPT2”. Check your documentation for your particular implementation.

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

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.

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

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

Structures and Unions

12.1 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 ” # & # 8��������