CISC105: Reading Notes
Tan and D'Orazio, Chapter 3

by Phill Conrad, Asst. Professor,
CIS Dept, University of Delaware

See reading notes for Chapter 1 for discussion of the purpose of these reading notes.

What to emphasize in Chapter 3

As with all the remaining chapters in the book, it is divided into Lessons where you study features of the C programming language one topic at a time, and Application Programs where you put together what you have learned into complete, useful programs.

From now on, I will refer to the lessons by numbers preceded with "L" (for example, L3.1 L3.2, L3.3, etc.) and application programs by numbers preceded with AP (for example, "AP3.1, AP3.2, etc.")

Like Chapter 2, most Chapter 3 is central to C Programming. However, we will defer one section until later in the course: we will skip over L3.9, and cover it after Chapter 6. The book itself suggests this as an option. So, you are not responsible for any material in Chapter 3.9 for now (and we might skip this material altogether.)

The remainder of Chapter 3 contains very basic and useful information about getting started with C programming. It is pretty well written and easy to follow, and you should read it through the other sections in their entirety.

Experimenting with the code from the textbook

You may want to have a computer terminal handy as you read, so that you can try out the programs that are given in the text as you read about them. You are more likely to succeed if you "play around" with the programs, experimenting with them as you learn about the C language. You can find the source code for these programs on the floppy disk that came with the textbook. You might make a separate directory under ~/cisc181 called ~/cisc181/td99, and then make a separate directory for each chapter, where you can play with the code examples, for example, using the commands:

mkdir ~/cisc181/td99
mkdir ~/cisc181/td99/ch03


Note that to use these programs on strauss, you may have to do two things:

  1. upload the files from a PC that has a floppy disk (for example, copy file L3_1.C from the subdirectory A:\Chapter3\Applications into some directory on strauss (e.g. your ~/cisc181/td99/ch03 directory)
  2. run the program "dos2unix" to fix up some weirdness that can cause the compiler to issue annoying messages like: warning: "illegal whitespace character in directive", and those annoying ^M characters that seem to show up in files copied from DOS or Windows disks. You can do this with the command:
    dos2unix L3_1.C L3_1.c
    Note that this creates a new copy of the file with a lowercase .c extension; this new copy is in the Unix file format.

Fortunately, I have already done most of that for you: you can find a copy of the code from the book where I have already done the dos2unix stuff in the following directory on strauss:

~pconrad/public_html/cisc105/td99/code

For example, consider the subdirectory:

~pconrad/public_html/cisc105/td99/code/Chapter3/Lessons

When logged into strauss, you can use the following sequence of commands to go to that directory, look at the files there, and copy one of the files into your ~/cisc181/td99/ch03 subdirectory. I made one typing error, and I left it in to show that this is "normal". Note that I also use the "mv" command as a way to "rename" the file to a name that is more suitable for Unix. (I already did the copy to strauss and the dos2unix command, but I didn't rename the files—this wasn't as easy to do from a script—so you'll have to do that part yourself.) In addition to changing the .C to a .c (which is required by the C compiler), I chose a name without underscores, because I find the period more convenient to type than the underscore; otherwise, there is no special reason I got rid of the underscore.

> cd
> mkdir ~/cisc105/td99
> mkdir ~/cisc105/td99/ch03
> cd ~pconrad/public_html/cisc105/td99/code
> ls
Chapter2 Chapter4 Chapter6 Chapter8 READ.ME
Chapter3 Chapter5 Chapter7 Chapter9
> cd Chapter3
> ls
Applications Lessons
> cd Lessons
> ls
C3_6.IN L3_2.C L3_4.C L3_6.C L3_8.C LE3_7_2.C
L3_1.C L3_3.C L3_5.C L3_7.C L3_9.C LE3_7_3.C
> cp L3.1C ~/cisc105/td99/ch03
cp: cannot access L3.1C
> cp L3_1.C ~/cisc105/td99/ch03
> cd ~/cisc105/td99/ch03
> ls
L3_1.C
> mv L3_1.C L3.1.c
> cc L3.1.c
> ./a.out
Month=12, Expense=$ 111.10
For the 11th month of the year
the expenses were $82.10
and the income was $100.00

>

These files are also available at the following URL:

http://udel.edu/~pconrad/public_html/cisc105/td99

 

Crucial Material: The following sections are crucial to your success in this course. Material from these sections will be useful not only in this course, but also in later courses as well. It definitely be on assignments and exams.

Sections 3.3 and 3.4.

Important Material: The following sections contain concepts and terminology that are important enough that they might appear on a quiz or exam, but are not as important as the information in the "Crucial Material" list.

Everything else in Chapter 3 except 3.9 (we are deferring 3.9 until later in the semester, or perhaps skipping it altogether).

Chapter 3, Tan and D'Orazio (1999)

Lesson 3.1: Data and Variables: Naming, Declaring, Assigning and Printing Values

This is an excellent treatment of this topic. Some key concepts you need to get from this lesson:

Of particular importance are point #3 on page 78-79, and point #6 on page 80. Please read these items over a couple of times slowly. If you really study these two paragraphs and understand them, it will help you immensely in understanding what is going on with C and C++ programs.

A comment about Tan and D'Orazio's rules for variables in Table 3.1. Most of what is in Table 3.1 is exactly right; the table gives you the C language rules for naming variables. They slipped in one "editorial comment" however, and its one that I think needs some clarification. The authors suggest that while use of "uppercase and mixed-case characters" is:

"allowed; however many programmers use lowercase characters for variable names and uppercase for constant names. Differentiate your identifiers by using different characters rather than different cases"

For the most part, I agree with this comment. For example, all of the following variables are legal, but are not examples of good programming style:

int wIdTh, HEIGHT, dEPTH, Volume;

The problem with these variables is that there is a "convention" that variables are in lowercase. When you violate that convention, you make programs that are more difficult to maintain for future programmers.

However, there is one exception to the rule of "all lowercase variables" that is worth mentioning: camel notation. As an alternative to using the underscore, as in:

int building_height, building_depth, building_width;

camel notation using mixed case, where the variable starts with a lowercase letter, but each new word uses uppercase, as in:

int buildingHeight, buildingDepth, buildingWidth;

Many programmers consider this perfectly acceptable style and some prefer it to the user of underscores. It is advisable, though, to either always use underscores, or always use camel notation within a given program; mixing the two makes the code harder to maintain.

Lesson 3.2 Constant Macros and More about Printing Variable Values

Lesson 3.2 focuses on two topics:

  1. constant macros. An example is:

    #define PI 3.14159

    The correct name is "constant macro", but these are typically just called "pound defines" by many programmers.
  2. field widths in format specifiers, such as "%8.2f", "%+5d", etc.

Here are some observations about each of these topics:

Constant Macros (also known as "pound defines")

A key point in this lesson is the use of #define for defining what are called "constant macros". Constant macros help you avoid "magic numbers" in your program. A "magic number" is some value that appears in your code without explanation and just somehow "makes the code work".

A good example of a magic number can be found in the scoring of Diving at the Athens 2004 Olympics. The web page for the 2004 games indicates that the final score for a dive is determined from the judges scores by the following algorithm, taken from the official Olympic web site at: http://www.athens2004.com/en/DivingRules

During the Olympic Games, World Championships and World Cups, the judging panel consists of seven judges. When the seven judges give their scores, the highest and the lowest scores are cancelled. The five remaining scores are added up and the total is then multiplied by the difficulty factor for each dive and then by 0.6 in order to arrive at the final result. If two competitors get the same total score they are considered tied.

Notice that the "magic number" 0.6 appears in this formula without explanation. There is probably some historical reason for this magic number 0.6, however to a casual observer, it appears to be an arbitrary choice. Presumably, at some point in the future, if judging criteria are changed, this number might be changed. As a result, we do not want to "hard code" the number in our program.

Therefore, to make this number (which might change later) stand out in the code, we instead put it at the very top of our program. We also give it a name, to indicate where it came from, and help the reader of the code understand what it represents.

 /* from rules on Athens 2004 Olympic web page, 
	 http://www.athens2004.com/en/DivingRules */
	 
 #define DIVING_SCORE_SCALING_FACTOR 0.6 
  

Then, in our formula, we refer to the number by this name:

 /* from rules on Athens 2004 Olympic web page, 
	 http://www.athens2004.com/en/DivingRules */
	 
 finalScore =  DIVING_SCORE_SCALING_FACTOR * degreeOfDifficulty * total;

Another good example is a tax rate. The sales tax varies from state to state. In Delaware, as of this writing (2004) it is zero! However, in Pennsylvania, it is currently 6%. If we want to write software that will work in both states, we can put a definition like the following at the top of our program:

#define SALES_TAX_RATE 0.00

In a formula, we would write:

total = price + (price * SALES_TAX_RATE);

(Here, the parentheses are unnecessary because of order of operations, but it is good style to include them anyway; it makes the formula easier to read.)

In Pennsylvania, we would then only have to change the one line of the program to:

#define SALES_TAX_RATE 0.06

The real advantage of this becomes clear when you have a large piece of software where SALES_TAX_RATE appears not just once, but several times in the code. Then, when you need to change the sales tax, you can just change it in one place, rather than in many different places.

Details of Format Specifiers (e.g. "%8.3f", "%+5d", etc.)

A key point is that one on p. 93: "Does all this need to be memorized? No."

The key word there is all. There is, in fact, a short list of facts you should memorize, and here it is:

  1. %d is used with int
  2. %f is used with float
  3. %lf is used with double
  4. With %d, you can use a single number such as %5d or %7d to specify a field width. If the number doesn't fill the whole field, the field is padded with space. If it is too big, the whole number is printed anyway.
  5. With %f and %lf, you can use a format such as %6.2f, %8.0f, or %9.1f. In each case, the first number specifies the size of the entire field, including the decimal point, and the numbers after the decimal point. The second number specifies the number of digits after the decimal point. So with %6.2f, for example, there are 2 digits after the decimal point, and one decimal point, so that leaves only three digits in front of the decimal point.

Later on, we'll learn some fun facts about %c and %s, which are used with the char data type, and the char * data type, respectively, but that's for a later chapter.

Lesson 3.3 Arithmetic Operators and Expressions

This lesson is very important stuff! Some things to particularly get out of this are:

Items 7 through 10 and Table 3.4 are particularly helpful in learning how to debug a certain type of error. I recommend that you read over them.


However, one thing to note: the textbook suggests that a division by zero error will likely produce an error message about an "overflow". On strauss, that is not necessarily the case. Instead, division by zero with integers will likely give a "floating exception" (strangely enough), while division by zero with the double data type produces a value that prints out as "Inf" (i.e., infinity.) Here is some sample code, and a sample script:

> cat divByZero.c
/* divByZero.c  P. Conrad 9/15/04 */

int main(void)
{
  int a=5;
  int b=0;
  int c;

  c = a/b;

  printf("Done; c=%d\n",c);
  
}
> cc divByZero.c
> ./a.out
Floating exception
> cat doubleDivByZero.c
/* divByZero.c  P. Conrad 9/15/04 */

int main(void)
{
  double a=5.0;
  double b=0.0;
  double c;

  c = a/b;

  printf("Done: c=%lf\n",c);
  
}
> cc doubleDivByZero.c
> ./a.out
Done: c=Inf
> 

 

Lesson 3.4 Mixed Type Arithmetic, Compound Assignment, Operator Precedence and Type Casting

This is another excellent writeup by the textbook authors about some very important topics. Please read this!

Some key topics:


One place where the textbook is slightly inconsistent


One slight inconsitency that I should point out though, so you won't get confused: item 16 on page 109 says that "if all arithmetic operators are of equal precedence in an arithmetic expression, the leftmost operator is executed first". That is true most of the time. However, if you look at the details in item 19 on page 110, you get the full story. Look just above table 3.6, where it explains that "The associativity specifies the direction of evaluation of operators with the same precedence." Note that while most operators associate L to R (meaning that item 16 is correct), a few associate R to L, including:

unary + and - (positive and negative operations)
unary ++ and -- (increment and decrement)
+=, -=, *=, /=, and %= (assignment with math operations)
= (assignment)

 

Lesson 3.5 Reading Data from the Keyboard

This section covers the details of the scanf statement. Some things to recall about the scanf operator:

Lesson 3.6 Reading Data from a File

I love the fact that this section comes early in the text. Most textbooks put this topic very late in the book. The fact that this topic comes so early in Tan and D'Orazio is one of the things that led me to select this book for our course.

Reading data from files allows us to process large volumes of data in the programs we write this semester—much more than would be reasonable for us to type in from the keyboard. For now, we will only read in a small number of items from the data files we work with. However, once we learn while loops and for loops (Chapter 4), we can process files containing hundreds, thousands, or even millions of data items with only a few lines of code.

However, I do have one quibble with the approach that Tan and D'Orazio take: they introduce files before they introduce the if statement. As a result, they omit a very important aspect of the discussion of working with files: the fact that after you do an fopen() operation, you should always check the result of that operation before proceeding.

For example, the example in Lesson 3.6 shows the following source code. See what happens if the file C3_6.IN doesn't exist (I rename C3_6.IN to the name FOO just to make sure the file doesn't exist when we run the program the second time.)

> ls
C3_6.IN  L3_1.c   L3_6.c   L3_6a.c  a.out
> cat L3_6.c
/* Lesson for 3_6 */

#include <stdio.h>
void main(void)
{
 double xx ;
 int   ii, kk; 
 FILE *inptr;
 
 inptr=fopen ("C3_6.IN","r"); 
 fscanf(inptr,"%d",&ii); 
 fscanf(inptr,"%d %lf",&kk,&xx);

 fclose(inptr); 

 printf("ii=%5d\nkk=%5d\nxx=%9.3lf\n",ii, kk, xx); 
}



> cc L3_6.c 
> ./a.out
ii=   36
kk=  123
xx=  456.780
> mv C3_6.IN FOO
> ./a.out
Segmentation fault
> 

The "segmentation fault" results because the variable "inptr" is a pointer variable. If the fopen() call is not successful in opening the file, then using "inptr" in a subsequent fscan operation results in a segmentation fault (access through a bad pointer.) We'll learn more about segmentation faults in a later chapter, but for now, even if we are not sure what a segmentation fault is, we can see that it is not a very "user-friendly" error to run into.

Here's a better approach. The changed code is in bold face type. We need to use an "if" statement to check whether "inptr" is equal to the value NULL (this is how fopen() tells us that the file open operation failed.) This allows us to make a much nicer error message when the file doesn't exist (or for whatever other reason, cannot be opened.)


> ls
C3_6.IN  L3_1.c   L3_6.c   L3_6a.c  a.out
> cat L3_6a.c 
/* Lesson for 3_6 */

#include <stdio.h>
void main(void)
{
 double xx ;
 int   ii, kk; 
 FILE *inptr;
 
 inptr=fopen ("C3_6.IN","r"); 
 if (inptr==NULL)
   {
     printf("Error: could not open file\n");
     exit(-1);
   }
 fscanf(inptr,"%d",&ii); 
 fscanf(inptr,"%d %lf",&kk,&xx);

 fclose(inptr); 

 printf("ii=%5d\nkk=%5d\nxx=%9.3lf\n",ii, kk, xx); 
}



> cc L3_6a.c 
> ./a.out
ii=   36
kk=  123
xx=  456.780
> mv C3_6.IN FOO
> ./a.out
Error: could not open file
> mv FOO C3_6.IN
> ./a.out
ii=   36
kk=  123
xx=  456.780
> 

 

Lesson 3.7 Math Library Functions

This section covers use of the math library (e.g. functions such as sqrt() for square root, fabs() for floating point absolute value, sin(), cos(), for sine and cosine, etc.

It also covers the range of various numeric data types other than plain old int, float and double.

Using the math library

When you use the math library on strauss, when you compile, you need to include "-lm" on the cc compile statement. For example, consider the example program in Lesson 3.7. Here is what happens when you try to compile that without the -lm option on the cc compile line, and then with the -lm option on the compile line:

> cc L3_7.c
Undefined                       first referenced
 symbol                             in file
exp                                 L3_7.o
log10                               L3_7.o
sqrt                                L3_7.o
sin                                 L3_7.o
log                                 L3_7.o
pow                                 L3_7.o
ld: fatal: Symbol referencing errors. No output written to a.out
> cc L3_7.c -lm
> ./a.out
x= 3.0    y= 4.0 

a=sin(x)    =      0.1411
b=exp(x)    =     20.0855
c=log(x)    = 1.098612289

d=sqrt(x)   =      1.7321
e=pow(x,y)  =     81.0000
f=sin(y)+exp(y)log10(y)*sqrt(y)/pow(3.2,4.4)=     53.8341

g=log(x)    = 1.098612309
> 

Without the -lm on the command line, the math library is not linked in, and the linking phase of the compiler can't find any machine language code for the routines such as sin(), exp(), etc. Hence, the error message tells us that these are "undefined symbols". When we include -lm, then everything works fine.

Other numeric data types (other than int, float and double)

You don't need to memorize all of the ranges on p. 129, especially since they are system dependent: they may be different on strauss from on any other computer you may encounter in "later life".

However, you should memorize the following facts:

Also, in addition to short int, int, and long int types, there is are two other integer data types in C not covered in your textbook (at least that I have found): "signed char" and "unsigned char", which can be used to store an 8 bit integer value (signed or unsigned.) This type is often used in programs that process multimedia data (e.g. GIF, JPEG, MP3, MPEG, WAV, etc.) to process binary data one byte (8-bits) at a time. We probably won't cover that topic in this course, but I want you to at least have "heard" of it.

Lesson 3.8 Writing Output to a File

This section gives a great overview of why we would want to write data directly to a file. I'll add only a few comments:

Lesson 3.9 Single Character Data

We are omitting this section until later in the semester. Look for notes on this chapter in the document that goes along with chapter 6.

Application Program 3.1: Area Calculation—Compound Operators and Program Development(1)

This is a very good program to look over. One thing to note however: the source code shows:

printf ("  \n\ 
First  triangle area = %6.2f   \n\ 
Second triangle area = %6.2f   \n\ 
Third  triangle area = %6.2f   \n\ 
Fourth triangle area = %6.2f   \n", 
area1, area2, area3, area4);  

This won't compile on strauss with either gcc or cc. Instead, change it to:

printf ("  \n"
  "First  triangle area = %6.2f   \n"
  "Second triangle area = %6.2f   \n" 
  "Third  triangle area = %6.2f   \n" 
  "Fourth triangle area = %6.2f   \n", 
   area1, area2, area3, area4);  

Note that in the corrected version, you don't see any commas after any of the character strings except for the last one. In C, when you have two character strings that are adjacent (separated only by "white space" like spaces and newlines) the compiler concatenates these together (attaches them to each other). The comma after the last string is the one that separates the format string (the concatenation of all the strings) from the list of variables.

Application Program 3.2 Temperature Units Conversion—Compound Operators and Program Development(2)

This is another good program to look over. One thing to note: this program repeats the formula for converting Celsius to Fahrenheit several times. At this stage in the semester, that's ok. Later on, however, after we've covered functions in more detail (i.e. following the treatment of Chapter 5), it would be considered a major style error to not create a "function" for this formula so that you don't need to repeat this formula over and over. In fact, if your instructor has already covered functions, you might consider how a program like this could be rewritten to look much simpler by using a function to do the conversion.

Application Program 3.3 Frictional Resistance—Arithmetic Operators, fscanf and constant macros

This is another good example program to look over. Again, there is one small change needed to make this program work on strauss. Instead of:

printf ("\n\ 
Block number         Force required for movement(KN)\n\n"); 

You should use the following:

printf ("\nBlock number         Force required for movement(KN)\n\n"); 

Exercises

The following exercises are definitely recommended, and are certainly fair game for exam questions:

You are not responsible (at this time) for exercises 1-5 on page 148 unless/until told otherwise by your instructor.

Application Exercises

All of the application exercises are good practice for you. A few of them may show up i your labs/projects. You are advised to look over the others, as they might make an appearance (in simplified form) as exam questions.

(end of reading notes for Chapter 3).

These reading notes are based on C Programming for Engineering and Computer Science, by H. H. Tan and T. B. D'Orazio, Copyright 1999 by WCB McGraw-Hill. The notes themselves are Copyright 2004, by Phillip T. Conrad, All Rights Reserved.

Prof. Conrad takes sole responsibility for any views or opinions expressed in these notes. Such view and opinions do necessarily reflect those of the University of Delaware, its Department of Computer and Information Sciences, or any other organization.