Programs with Selection Logic - C Programming For Beginners (2015)

C Programming For Beginners (2015)

CHAPTER 4

Programs with Selection Logic

In this chapter, we will explain the following:

§ What are Boolean expressions

§ How C represents Boolean values

§ How to write programs using if

§ How to write programs using if...else

§ Where semicolons are required, where they are optional and where they must not be put

§ How a program should be tested

§ Why symbolic constants are useful and how to use them in a C program

4.1 Introduction

In the last chapter, we showed how to write programs using sequence logic—programs whose statements are executed “in sequence” from the first to the last.

In this chapter, the programs will use selection logic—they will test some condition and take different courses of action based on whether the condition is true or false. In C, selection logic is implemented using the if and the if...else statements.

4.2 Boolean Expressions

A Boolean expression (named after the famous English mathematician George Boole) is one that is either true or false. The simplest kinds of Boolean expressions are those that compare one value with another. Some examples are:

k is equal to 999

a is greater than 100

a2 + b2 is equal to c2

b2 is greater than or equal to 4ac

s is not equal to 0

Each of these can be either true or false. These are examples of a special kind of Boolean expression called relational expressions. Such expressions simply check if one value is equal to, not equal to, greater than, greater than or equal to, less than and less than or equal to another value. We write them using relational operators.

The C relational operators (with examples) are:

==

equal to

k == 999, a*a + b*b == c*c

!=

not equal to

s != 0, a != b + c

>

greater than

a > 100

>=

greater than or equal to

b*b >= 4.0*a*c

<

less than

n < 0

<=

less than or equal to

score <= 65

Boolean expressions are normally used to control the flow of program execution. For example, we may have a variable (h, say) which starts off with a value of 0. We keep increasing it by 1 and we want to know when its value reaches 100. We say we wish to know when the condition h == 100 is true. A condition is the common name for a Boolean expression.

The real power of programming lies in the ability of a program to test a condition and decide whether it is true or false. If it is true, the program can perform one set of actions and if it is false, it can perform another set, or simply do nothing at all.

For example, suppose the variable score holds the score obtained by a student in a test, and the student passes if her score is 50 or more and fails if it is less than 50. A program can be written to test the condition

score >= 50

If it is true, the student passes; if it is false, the student fails. In C, this can be written as:

if (score >= 50) printf("Pass\n");

else printf("Fail\n");

When the computer gets to this statement, it compares the current value of score with 50. If the value is greater than or equal to 50, we say that the condition score >= 50 is true. In this case the program prints Pass. If the value of score is less than 50, we say that the condition score >= 50 is false. In this case, the program prints Fail.

In this chapter, we will see how Boolean expressions are used in if and if...else statements and, in the next chapter, we will see how they are used in while statements.

4.2.1 AND, &&

With the relational operators, we can create simple conditions. But sometimes, we need to ask if one thing is true AND another thing is true. We may also need to know if one of two things is true. For these situations, we need compound conditions. To create compound conditions, we use the logical operators AND, OR and NOT.

For example, suppose we want to know if the value of h lies between 1 and 99, inclusive. We want to know if h is greater than or equal to 1 AND if h is less than or equal to 99. In C, we express this as:

(h >= 1) && (h <= 99)

In C, the symbol for AND is &&.

Note the following:

§ The variable h must be repeated in both conditions. It is tempting, but wrong, to write

§ h >= 1 && <= 99 //this is wrong

§ The brackets around h >= 1 and h <= 99 are not required, but it is not wrong to put them. This is so since && (and ||, see next) have lower precedence than the relational operators. Without the brackets,

§ h >= 1 && h <= 99

§ would be interpreted by C like this:

§ (h >= 1) && (h <= 99)

§ This is the same as with the brackets.

4.2.2 OR, ||

If n is an integer representing a month of the year, we can check if n is invalid by testing if n is less than 1 OR n is greater than 12. In C, we express this as:

(n < 1) || (n > 12)

In C, the symbol for OR is ||. As discussed above, the brackets are not required and we could write the expression as

n < 1 || n > 12

This tests if n is invalid. Of course, we can test if n is valid by testing if

n >= 1 && n <= 12

Which test we use depends on how we wish to express our logic. Sometimes it’s convenient to use the valid test, sometimes the invalid one.

4.2.3 NOT, !

If p is some Boolean expression, then NOT p reverses the truth value of p. In others words, if p is true then NOT p is false; if p is false then NOT p is true. In C, the symbol for NOT is the exclamation mark, !. Using the example above, since

n >= 1 && n <= 12

tests for valid n, the condition NOT (n >=1 && n <= 12) tests for invalid n. This is written in C as

!(n >= 1 && n <= 12)

This is equivalent to n < 1 || n > 12. Those familiar with de Morgan's laws will know that

not (a and b) = (not a) or (not b)

and

not(a or b) = (not a) and (not b)

In general, if p and q are Boolean expressions, we have the following:

§ p && q is true when both p and q are true and false, otherwise;

§ p || q is true when either p or q is true and false only when both p and q are false;

§ !p is true when p is false and false when p is true.

This is summarized in the following table (with T for true and F for false):

p

q

&&

||

!p

T

T

T

T

F

T

F

F

T

F

F

T

F

T

T

F

F

F

F

T

Most of the programs in this book will use simple conditions. A few will use compound conditions.

The data type bool in C99

The original C standard and the later ANSI C standard did not define a Boolean data type. Traditionally, C has used the concept of the value of an expression to denote true/false. A numeric expression can be used in any context where a true/false value is required. The expression is considered true if its value is non-zero and false if its value is 0.

The latest C99 standard defines the type bool. However, in this book, we will use the traditional approach mainly because many popular C compilers do not support the C99 standard as yet. Also, as you will see, we can easily live without bool. The vast majority of our Boolean expressions would be relational expressions used in if and while statements. If we ever need a ‘Boolean’ variable, we can use an int variable with 1 representing true and 0representing false.

4.3 The if Construct

Let us write a program for the following problem:

A computer repair shop charges $100 per hour for labour plus the cost of any parts used in the repair. However, the minimum charge for any job is $150. Prompt for the number of hours worked and the cost of parts (which could be $0) and print the charge for the job.

We will write the program so that it works as follows:

Hours worked? 2.5

Cost of parts? 20

Charge for the job: $270.00

or

Hours worked? 1

Cost of parts? 25

Charge for the job: $150.00

The following algorithm describes the steps required to solve the problem:

prompt for and read the hours worked

prompt for and read the cost of parts

calculate charge = hours worked * 100 + cost of parts

if charge is less than 150 then set charge to 150

print charge

This is another example of an algorithm written in pseudocode—an informal way of specifying programming logic.

The algorithm introduces a new statement—the if statement. The expression

charge is less than 150

is an example of a condition. If the condition is true, the statement after then (called the then part) is executed; if it is false, the statement after then is not executed.

Program P4.1 shows how to express this algorithm as a C program.

Program P4.1

//print job charge based on hours worked and cost of parts

#include <stdio.h>

int main() {

double hours, parts, jobCharge;

printf("Hours worked? ");

scanf("%lf", &hours);

printf("Cost of parts? ");

scanf("%lf", &parts);

jobCharge = hours * 100 + parts;

if (jobCharge < 150) jobCharge = 150;

printf("\nCharge for the job: $%3.2f\n", jobCharge);

}

For this program, we choose to use 3 variables—hours, parts and jobCharge, all of type double since we may need to enter floating-point values for hours worked and cost of parts.

It is very important that you make an extra effort to understand the if statement since it is one of the most important statements in programming. It is the if statement that can make a program appear to think.

The condition

charge is less than 150

of the pseudocode algorithm is expressed in our program as

jobCharge < 150

When the program is executed, the job charge is calculated in the normal way (hours * 100 + parts). The if statement then tests if this value, jobCharge, is less than 150; if it is, then jobCharge is set to 150. If it is not less than 150, jobCharge remains as it is. The statement

if (jobCharge < 150) jobCharge = 150;

is a simple example of the if construct. Observe that the word then is not used in C. In general, the construct takes the following form in C:

if (<condition>) <statement>

The word if and the brackets around <condition> are required by C. You must supply <condition> and <statement> where <condition> is a Boolean expression and <statement> can be either a one-line statement or a block—one or more statements enclosed by { and }. If <condition> is true, <statement> is executed; if <condition> is false, <statement> is not executed. In either case, the program continues with the statement, if any, after <statement>.

In the program, <condition> is

jobCharge < 150

and <statement> is

jobCharge = 150;

To give an example where <statement> is a block, suppose we want to exchange the values of two variables a and b but only if a is bigger than b. This can be done with the following assuming, as an example, that a = 15, b = 8, and cis a temporary variable:

if (a > b)

{

c = a; //store a in c; c becomes 15

a = b; //store b in a; a becomes 8

b = c; //store old value of a, 15,in b

}

Here, <statement> is the part from { to }, a block containing 3 assignment statements. If a is greater than b, the block is executed (and the values are exchanged); if a is not greater than b, the block is not executed (and the values remain as they are). In passing, be aware that exchanging the values of two variables requires three assignment statements; it cannot be done with two. If you are not convinced, try it.

In general, if there are several things that we want to do if a condition is true, we must enclose them within { and } to create a block. This will ensure that we satisfy C’s rule that <statement> is a single statement or a block.

It is good programming practice to indent the statements in the block. This makes it easy to see at a glance which statements are in the block. If we had written the above as follows, the structure of the block would not be so easy to see:

if (a > b)

{

c = a; //store a in c; c becomes 15

a = b; //store b in a; a becomes 8

b = c; //store old value of a, 15,in b

}

When we are writing pseudocode, we normally use the following format:

if <condition> then

<statement1>

<statement2>

etc.

endif

The construct is terminated with endif, a convention used by many programmers. Note, again, that we indent the statements to be executed if <condition> is true. We emphasize that endif is not a C word but merely a convenient word used by programmers in writing pseudocode.

The example illustrates one style of writing a block in an if statement. This style matches { and } as follows:

if (<condition>)

{

<statement1>;

<statement2>;

etc.

}

Here, { and } line up with if and the statements are indented. This makes it easy to recognize what’s in the body. For a small program, it probably doesn’t matter, but as program size increases, it will become more important for the layout of the code to reflect its structure. In this book, we will use the following style (as you would know by now, the compiler doesn’t care which style is used):

if (<condition>) {

<statement1>;

<statement2>;

etc.

}

We will put { on the first line after the right bracket and let } match up with if; the statements in the block are indented. We believe this is as clear as the first style and it’s one less line in the program! Which style you use is a matter of personal preference; choose one and use it consistently.

4.3.1 Find the Sum of Two Lengths

Suppose that a length is given in metres and centimetres, for example, 3m 75cm. You are given two pairs of integers representing two lengths. Write a program to prompt for two lengths and print their sum such that the centimetre value is less than 100.

For example, the sum of 3m 25cm and 2m 15cm is 5m 40cm, but the sum of 3m 75cm and 5m 50cm is 9m 25cm.

Assume the program works as follows:

Enter values for m and cm: 3 75

Enter values for m and cm: 5 50

Sum is 9m 25cm

Observe that the data must be entered with digits only. If, for instance, we type 3m 75cm we will get an error since 3m is not a valid integer constant. Our program will assume that the first number entered is the metre value and the second number is the centimetre value.

We find the sum by adding the two metre values and adding the two centimetre values. If the centimetre value is less than 100, there is nothing more to do. But if it is not, we must subtract 100 from it and add 1 to the metre value. This logic is expressed as follows:

m = sum of metre values

cm = sum of centimetre values

if cm >= 100 then

subtract 100 from cm

add 1 to m

endif

As a boundary case, we must check that our program works if cm is exactly 100. As an exercise, verify that it does.

Program P4.2 solves the problem as described.

Program P4.2

//find the sum of two lengths given in metres and cm

#include <stdio.h>

int main() {

int m1, cm1, m2, cm2, mSum, cmSum;

printf("Enter values for m and cm: ");

scanf("%d %d", &m1, &cm1);

printf("Enter values for m and cm: ");

scanf("%d %d", &m2, &cm2);

mSum = m1 + m2; //add the metres

cmSum = cm1 + cm2; //add the centimetres

if (cmSum >= 100) {

cmSum = cmSum - 100;

mSum = mSum + 1;

}

printf("\nSum is %dm %dcm\n", mSum, cmSum);

}

We use the variables m1 and cm1 for the first length, m2 and cm2 for the second length, and mSum and cmSum for the sum of the two lengths.

The program assumes that the centimetre part of the given lengths is less than 100 and it works correctly if this is so. But what if the lengths were 3m 150cm and 2m 200cm?

The program will print 6m 250cm. (As an exercise, follow the logic of the program to see why.) While this is correct, it is not in the correct format since we require the centimetre value to be less than 100. We can modify our program to work in these cases as well by using integer division and % (the remainder operator).

The following pseudocode shows how:

m = sum of metre values

cm = sum of centimetre values

if cm >= 100 then

add cm / 100 to m

set cm to cm % 100

endif

Using the above example, m is set to 5 and cm is set to 350. Since cm is greater than 100, we work out 350 / 100 (this finds how many 100s there are in cm ) which is 3, using integer division; this is added to m, giving 8. The next line sets cm to 350 % 100 which is 50. So the answer we get is 8m 50cm, which is correct and in the correct format.

Note that the statements in the ‘then part’ must be written in the order shown. We must use the (original) value of cm to work out cm / 100 before changing it in the next statement to cm % 100. As an exercise, work out what value will be computed for the sum if these statements are reversed. (The answer will be 5m 50cm, which is wrong. Can you see why?)

These changes are reflected in Program P4.3.

Program P4.3

//find the sum of two lengths given in metres and cm

#include <stdio.h>

int main() {

int m1, cm1, m2, cm2, mSum, cmSum;

printf("Enter values for m and cm: ");

scanf("%d %d", &m1, &cm1);

printf("Enter values for m and cm: ");

scanf("%d %d", &m2, &cm2);

mSum = m1 + m2; //add the metres

cmSum = cm1 + cm2; //add the centimetres

if (cmSum >= 100) {

mSum = mSum + cmSum / 100;

cmSum = cmSum % 100;

}

printf("\nSum is %dm %dcm\n", mSum, cmSum);

}

The following is a sample run of this program:

Enter values for m and cm: 3 150

Enter values for m and cm: 2 200

Sum is 8m 50cm

The astute reader may recognize that we do not even need the if statement.

Consider this:

mSum = m1 + m2; //add the metres

cmSum = cm1 + cm2; //add the centimetres

mSum = mSum + cmSum / 100;

cmSum = cmSum % 100;

where the last two statements come from the if statement.

We know therefore that this will work if cmSum is greater than or equal to 100 since, when that is the case, these four statements are executed.

What if cmSum is less than 100? Originally, the last two statements would not have been executed since the if condition would have been false. Now they are executed. Let us see what happens. Using the example of 3m 25cm and 2m 15cm, we get mSum as 5 and cmSum as 40.

In the next statement 40 / 100 is 0 so mSum does not change and in the last statement 40 % 100 is 40 so cmSum does not change. So the answer will be printed correctly as

Sum is 5m 40cm

You should begin to realize by now that there is usually more than one way to express the logic of a program. With experience and study, you will learn which ways are better and why.

4.4 The if...else Construct

Let us write a program for the following problem:

A student is given 3 tests, each marked out of 100. The student passes if his average mark is greater than or equal to 50 and fails if his average mark is less than 50. Prompt for the 3 marks and print Pass if the student passes and Fail if he fails.

We will write the program assuming it works as follows:

Enter 3 marks: 60 40 56

Average is 52.0 Pass

or

Enter 3 marks: 40 60 36

Average is 45.3 Fail

The following algorithm describes the steps required to solve the problem:

prompt for the 3 marks

calculate the average

if average is greater than or equal to 50 then

print "Pass"

else

print "Fail"

endif

The part from if to endif is an example of the if...else construct.

The condition

average is greater than or equal to 50

is another example of a relational expression. If the condition is true, the statement after then (the then part) is executed; if it is false, the statement after else (the else part) is executed.

The whole construct is terminated with endif.

When you write pseudocode, what is important is that the logic intended is unmistakably clear. Note again how indentation can help by making it easy to identify the then part and the else part.

In the end, though, you must express the code in some programming language for it to be run on a computer. Program P4.4 shows how to do this for the above algorithm.

Program P4.4

//request 3 marks; print their average and Pass/Fail

#include <stdio.h>

int main() {

int mark1, mark2, mark3;

double average ;

printf("Enter 3 marks: ");

scanf("%d %d %d", &mark1, &mark2, &mark3);

average = (mark1 + mark2 + mark3) / 3.0;

printf("\nAverage is %3.1f", average);

if (average >= 50) printf(" Pass\n");

else printf(" Fail\n");

}

Study carefully the if...else construct in the program. It reflects the logic expressed on the previous page. Note, again, that the word then is omitted in C.

In general, the if...else construct in C takes the form shown below.

if (<condition>) <statement1> else <statement2>

The words if and else, and the brackets, are required by C. You must supply <condition>, <statement1> and <statement2>. Each of <statement1> and <statement2> can be a one-line statement or a block. If <condition> is true, <statement1> is executed and <statement2> is skipped; if <condition> is false, <statement1> is skipped and <statement2> is executed. When the if construct is executed, either <statement1> or <statement2> is executed, but not both.

If <statement1> and <statement2> are one-line statements, you can use this layout:

if (<condition>) <statement1>

else <statement2>

If <statement1> and <statement2> are blocks, you can use the following layout:

if (<condition>) {

...

}

else {

...

}

In describing the various constructs in C, we normally use the phrase “where <statement> can be a one-line statement or a block”.

It is useful to remember that, in C, for one-line statements, the semicolon is considered part of the statement. Examples are:

a = 5;

printf("Pass\n");

scanf("%d", &n);

So, in those cases where one-line statements are used, the semicolon, being part of the statement, must be present. In Program P4.4, in the if...else statement,

<statement1> is

printf("Pass\n");

and <statement2> is

printf("Fail\n");

However, for a block or compound statement, the right brace, }, ends the block. So, in those cases where a block is used, there is no need for an additional semicolon to end the block.

It is sometimes useful to remember that the entire if...else construct (from if to <statement2>) is considered by C to be one statement and can be used in any place where one statement is required.

4.4.1 Calculate Pay

For an example that requires blocks, suppose we have values for hours worked and rate of pay (the amount paid per hour) and wish to calculate a person’s regular pay, overtime pay and gross pay based on the following:

If hours worked is less than or equal to 40, regular pay is calculated by multiplying hours worked by rate of pay and overtime pay is 0. If hours worked is greater than 40, regular pay is calculated by multiplying 40 by the rate of pay and overtime pay is calculated by multiplying the hours in excess of 40 by the rate of pay by 1.5. Gross pay is calculated by adding regular pay and overtime pay.

For example, if hours is 36 and rate is 20 dollars per hour, regular pay is $720 (36 times 20) and overtime pay is $0. Gross pay is $720.

And if hours is 50 and rate is 12 dollars per hour, regular pay is $480 (40 times 12) and overtime pay is $180 (excess hours 10 times 12 times 1.5). Gross pay is $660 (480 + 180).

The above description could be expressed in pseudocode as follows:

if hours is less than or equal to 40 then

set regular pay to hours x rate

set overtime pay to 0

else

set regular pay to 40 x rate

set overtime pay to (hours – 40) x rate x 1.5

endif

set gross pay to regular pay + overtime pay

We use indentation to highlight the statements to be executed if the condition “hours is less than or equal to 40” is true and those to be executed if the condition is false. The whole construct is terminated with endif.

The next step is to convert the pseudocode to C. When we do, we have to make sure that we stick to C’s rules for writing an if...else statement. In this example, we have to ensure that both the then and else parts are written as blocks since they both consist of more than one statement.

Using the variables hours (hours worked), rate (rate of pay), regPay (regular pay), ovtPay (overtime pay) and grossPay (gross pay), we write the C code, thus:

if (hours <= 40) {

regPay = hours * rate;

ovtPay = 0;

} //no semicolon here; } ends the block

else {

regPay = 40 * rate;

ovtPay = (hours - 40) * rate * 1.5;

} //no semicolon here; } ends the block

grossPay = regPay + ovtPay;

Note the two comments. It would be wrong to put a semicolon after the first } since the if statement continues with an else part. If we were to put one, it effectively ends the if statement and C assumes there is no else part. When it finds the word else, there will be no if with which to match it and the program will give a “misplaced else” error.

There is no need for a semicolon after the last } but putting one would do no harm.

Problem: write a program to prompt for hours worked and rate of pay. The program then calculates and prints regular pay, overtime pay and gross pay, based on the above description.

The following algorithm outlines the overall logic of the solution:

prompt for hours worked and rate of pay

if hours is less than or equal to 40 then

set regular pay to hours x rate

set overtime pay to 0

else

set regular pay to 40 x rate

set overtime pay to (hours – 40) x rate x 1.5

endif

set gross pay to regular pay + overtime pay

print regular pay, overtime pay and gross pay

This algorithm is implemented as Program P4.5. All the variables are declared as double so that fractional values can be entered for hours worked and rate of pay.

Program P4.5

#include <stdio.h>

int main() {

double hours, rate, regPay, ovtPay, grossPay;

printf("Hours worked? ");

scanf("%lf", &hours);

printf("Rate of pay? ");

scanf("%lf", &rate);

if (hours <= 40) {

regPay = hours * rate;

ovtPay = 0;

}

else {

regPay = 40 * rate;

ovtPay = (hours - 40) * rate * 1.5;

}

grossPay = regPay + ovtPay;

printf("\nRegular pay: $%3.2f\n", regPay);

printf("Overtime pay: $%3.2f\n", ovtPay);

printf("gross pay: $%3.2f\n", grossPay);

}

A sample run of this program is shown here:

Hours worked? 50

Rate of pay? 12

Regular pay: $480.00

Overtime pay: $180.00

Gross pay: $660.00

You should verify that the results are indeed correct.

Note that even though hours and rate are double, data for them can be supplied in any valid numeric format—here we use the integers 50 and 12. These values would be converted to double format before being stored in the variables. We could, if we wished, have typed 50.0 and 12.00, for example.

4.5 On Program Testing

When we write a program we should test it thoroughly to ensure that it is working correctly. As a minimum, we should test all paths through the program. This means that our test data must be chosen so that each statement in the program is executed at least once.

For Program P4.5, the sample run tests only when the hours worked is greater than 40. Based on this test alone, we cannot be sure that our program will work correctly if the hours worked is less than or equal to 40. To be sure, we must run another test in which the hours worked is less than or equal to 40. The following is such a sample run:

Hours worked? 36

Rate of pay? 20

Regular pay: $720.00

Overtime pay: $0.00

Gross pay: $720.00

These results are correct which gives us greater assurance that our program is correct. We should also run a test when the hours is exactly 40; we must always test a program at its ‘boundaries’. For this program, 40 is a boundary—it is the value at which overtime begins to be paid.

What if the results are incorrect? For example, suppose overtime pay is wrong. We say the program contains a bug (an error), and we must debug (remove the error from) the program. In this case, we can look at the statement(s) which calculate the overtime pay to see if we have specified the calculation correctly. If this fails to uncover the error, we must painstakingly ‘execute’ the program by hand using the test data which produced the error. If done properly, this will usually reveal the cause of the error.

4.6 Symbolic Constants

In Program 4.1, we used two constants—100 and 150—denoting the labour charge per hour and the minimum job cost, respectively. What if these values change after the program has been written? We would have to find all occurrences of them in the program and change them to the new values.

This program is fairly short so this would not be too difficult to do. But imagine what the task would be like if the program contained hundreds or even thousands of lines of code. It would be difficult, time-consuming and error-prone to make all the required changes.

We can make life a little easier by using symbolic constants (also called manifest or named constants)—identifiers which we set to the required constants in one place. If we need to change the value of a constant, the change would have to be made in one place only. For example, in Program P4.1, we could use the symbolic constants ChargePerHour and MinJobCost. We would set ChargePerHour to 100 and MinJobCost to 150.

In C, we use the #define directive to define symbolic constants, among other uses. We show how by rewriting Program P4.1 as Program P4.6.

Program P4.6

//This program illustrates the use of symbolic constants

//Print job charge based on hours worked and cost of parts

#include <stdio.h>

#define ChargePerHour 100

#define MinJobCost 150

int main() {

double hours, parts, jobCharge;

printf("Hours worked? ");

scanf("%lf", &hours);

printf("Cost of parts? ");

scanf("%lf", &parts);

jobCharge = hours * ChargePerHour + parts;

if (jobCharge < MinJobCost) jobCharge = MinJobCost;

printf("\nCharge for the job: $%3.2f\n", jobCharge);

}

4.6.1 The #define Directive

Directives in C normally come at the top of the program. For our purposes, the #define directive takes the following form:

#define identifier followed by the “replacement text”

In the program, we used

#define ChargePerHour 100

Note that this is not a normal C statement and a semicolon is not needed to end it. Here, the identifier is ChargePerHour and the replacement text is the constant 100. In the body of the program, we use the identifier instead of the constant.

When the program is compiled, C performs what is called a “pre-processing” step. It replaces all occurrences of the identifier by its replacement text. In program P4.6, it replaces all occurrences of ChargePerHour by 100 and all occurrences of MinJobCost by 150 . After this is done, the program is compiled. It is up to the programmer to ensure that, when the identifier is replaced, the resulting statement makes sense.

Effectively, the directives say that the identifier ChargePerHour is equivalent to the constant 100 and the identifier MinJobCost is equivalent to 150.

For example, the pre-processing step changes

if (jobCharge < MinJobCost) jobCharge = MinJobCost;

to

if (jobCharge < 150) jobCharge = 150;

Suppose, for instance, that the minimum job cost changes from 150 to 180. We would just need to change the value in the #define directive, thus:

#define MinJobCost 180

No other changes would be needed.

In this book, we will use the convention of starting a symbolic constant identifier with an uppercase letter. Note, however, that C allows you to use any valid identifier.

4.6.2 Example – Symbolic Constants

For a slightly bigger example, consider program P4.5. There, we used two constants—40 and 1.5—denoting the maximum regular hours and the overtime rate factor, respectively. We rewrite program P4.5 as program P4.7 using the symbolic constants MaxRegularHours (set to 40) and OvertimeFactor (set to 1.5).

Program P4.7

#include <stdio.h>

#define MaxRegularHours 40

#define OvertimeFactor 1.5

int main() {

double hours, rate, regPay, ovtPay, grossPay;

printf("Hours worked? ");

scanf("%lf", &hours);

printf("Rate of pay? ");

scanf("%lf", &rate);

if (hours <= MaxRegularHours) {

regPay = hours * rate;

ovtPay = 0;

}

else {

regPay = MaxRegularHours * rate;

ovtPay = (hours - MaxRegularHours) * rate * OvertimeFactor;

}

grossPay = regPay + ovtPay;

printf("\nRegular pay: $%3.2f\n", regPay);

printf("Overtime pay: $%3.2f\n", ovtPay);

printf("Gross pay: $%3.2f\n", grossPay);

}

Suppose, for instance, the maximum regular hours changes from 40 to 35. Program P4.7 would be easier to change than Program P4.5, since we would need to change the value in the #define directive only, like this:

#define MaxRegularHours 35

No other changes would be needed.

The numbers 40 and 1.5 used in Program P4.5 are referred to as magic numbers—they appear in the program for no apparent reason, as if by magic. Magic numbers are a good sign that a program may be restrictive, tied to those numbers. As far as possible, we must write our programs without magic numbers. Using symbolic constants can help to make our programs more flexible and easier to maintain.

4.7 More Examples

We now write programs to solve two more problems. Their solutions will illustrate how to use if...else statements to determine which of several alternatives to take. In the sample runs, the underlined items are typed by the user; everything else is printed by the computer.

4.7.1 Print a Letter Grade

Write a program to request a score in a test and print a letter grade based on the following:

score < 50

F

50 <= score < 75

B

score >= 75

A

The program should work as follows:

Enter a score: 70

Grade B

A solution is shown as Program P4.8.

Program P4.8

//request a score; print letter grade

#include <stdio.h>

int main() {

int score;

printf("Enter a score: ");

scanf("%d", &score);

printf("\nGrade ");

if (score < 50) printf("F\n");

else if (score < 75) printf("B\n");

else printf("A\n");

}

The second printf prints a blank line followed by the word Grade followed by one space but does not end the line. When the letter grade is determined, it will be printed on this same line.

We saw that the if...else statement takes the form

if (<condition>) <statement1> else <statement2>

where <statement1> and <statement2> can be any statements. In particular, either one (or both) can be an if...else statement. This allows us to write so-called nested if statements. This is especially useful when we have several related conditions to test, as in this example. In the program, we can think of the part:

if (score < 50) printf("F\n");

else if (score < 75) printf("B\n");

else printf("A\n");

as

if (score < 50) printf("F\n");

else <statement>

where <statement> is this if...else statement:

if (score < 75) printf("B\n");

else printf("A\n");

If score is less than 50, the program prints F and ends. If not, it follows that score must be greater than or equal to 50.

Knowing this, the first else part checks if score is less than 75. If it is, the program prints B and ends. If not, it follows that score must be greater than or equal to 75.

Knowing this, the second else part (else printf("A\n"); which matches the second if) prints A and ends.

To make sure the program is correct, you should run it with at least 3 different scores (e.g. 70, 45, 83) to verify that each of the 3 grades is printed correctly. You should also test it at the 'boundary' numbers, 50 and 75.

Note the preferred style for writing else if’s. If we had followed our normal indenting style, we would have written

if (score < 50) printf("F\n");

else

if (score < 75) printf("B\n");

else printf("A\n");

This, of course, would still be correct. However, if we had more cases, the indentation would go too deep and would look awkward. Also, since the different ranges for score are really alternatives (rather than one being within the other), it is better to keep them at the same indentation level.

The statements here were all one-line printf statements so we chose to write them on the same line as if and else. However, if they were blocks, it would be better to write it like this:

if (score < 50) {

...

}

else if (score < 75) {

...

}

else {

...

}

As an exercise, modify the program to print the correct grade based on the following:

score < 50

F

50 <= score < 65

C

50 <= score < 80

B

score >= 80

A

4.7.2 Classify a Triangle

Given three integer values representing the sides of a triangle, print:

§ Not a triangle if the values cannot be the sides of any triangle. This is so if any value is negative or zero, or if the length of any side is greater than or equal to the sum of the other two;

§ Scalene if the triangle is scalene (all sides different);

§ Isosceles if the triangle is isosceles (two sides equal);

§ Equilateral if the triangle is equilateral (three sides equal).

The program should work as follows:

Enter 3 sides of a triangle: 7 4 7

Isosceles

A solution is shown as Program P4.9.

Program P4.9

//request 3 sides; determine type of triangle

#include <stdio.h>

int main() {

int a, b, c;

printf("Enter 3 sides of a triangle: ");

scanf("%d %d %d", &a, &b, &c);

if (a <= 0 || b <= 0 || c <= 0) printf("\nNot a triangle\n");

else if (a >= b + c || b >= c + a || c >= a + b)

printf("\nNot a triangle\n");

else if (a == b && b == c) printf("\nEquilateral\n");

else if (a == b || b == c || c == a) printf("\nIsosceles\n");

else printf("\nScalene\n");

}

The first task is to establish that we, in fact, have a valid triangle. The first if checks if any of the sides is negative or zero. If so, Not a triangle is printed. If they are all positive, we go to the else part which itself consists of an if...else statement.

Here, the if checks if any one side is greater than or equal to the sum of the other two. If so, Not a triangle is printed. If not, then we have a valid triangle and must determine its type by executing the else part beginning

if (a == b ...

It is easiest to do this by first checking if it is equilateral. If two different pairs of sides are equal—if (a == b && b == c)—then all three are equal and we have an equilateral triangle.

If it is not equilateral, then we check if it is isosceles. If any two sides are equal—if (a == b || b == c || c == a)—we have an isosceles triangle.

If it is neither equilateral nor isosceles, then it must be scalene.

As an exercise, modify the program to determine if the triangle is right-angled. It is right-angled if the sum of the squares of two sides is equal to the square of the third side.

EXERCISES 4

1. An auto repair shop charges as follows. Inspecting the vehicle costs $75. If no work needs to be done, there is no further charge. Otherwise, the charge is $75 per hour for labour plus the cost of parts, with a minimum charge of $120. If any work is done, there is no charge for inspecting the vehicle.

2. Write a program to read values for hours worked and cost of parts (either of which could be 0) and print the charge for the job.

3. Write a program which requests two weights in kilograms and grams and prints the sum of the weights. For example, if the weights are 3kg 500g and 4kg 700g, your program should print 8kg 200g.

4. Write a program which requests two lengths in feet and inches and prints the sum of the lengths. For example, if the lengths are 5 ft. 4 in. and 8 ft. 11 in., your program should print 14ft. 3 in. (1 ft. = 12 in.)

5. A variety store gives a 15% discount on sales totalling $300 or more. Write a program to request the cost of 3 items and print the amount the customer must pay.

6. Write a program to read two pairs of integers. Each pair represents a fraction. For example, the pair 3 5 represents the fraction 3/5. Your program should print the sum of the given fractions. For example, give the pairs 3 5and 2 3, your program should print 19/15, since 3/5 + 2/3 = 19/15.

7. Modify the program so that it prints the sum with the fraction reduced to a proper fraction; for this example, your program should print 1 4/15.

8. Write a program to read a person’s name, hours worked, hourly rate of pay and tax rate (a number representing a percentage, e.g. 25 meaning 25%). The program must print the name, gross pay, tax deducted and gross pay.

9. Gross pay is calculated as described in Section 4.3.1. The tax deducted is calculated by applying the tax rate to 80% of gross pay. And the net pay is calculated by subtracting the tax deducted from the gross pay.

10. For example, if the person works 50 hours at $20/hour and the tax rate is 25%, his gross pay would be (40 x 20) + (10 20 1.5) = $1100. He pays 25% tax on 80% of $1100, that is, 25% of $880 = $220. His net pay is 1100 - 220 = $880.

11. Write a program to read integer values for month and year and print the number of days in the month. For example, 4 2005 (April 2005) should print 30, 2 2004 (February 2004) should print 29 and 2 1900 (February 1900) should print 28.

12. A leap year, n, is divisible by 4; however, if n is divisible by 100 then it is a leap year only if it is also divisible by 400. So 1900 is not a leap year but 2000 is.

13. In an English class, a student is given 3 term tests (marked out of 25) and an end-of-term test (marked out of 100). The end-of-term test counts the same as the 3 term tests in determining the final mark (out of 100). Write a program to read marks for the 3 term tests followed by the mark for the end-of-term test. The program then prints the final mark and an indication of whether the student passes or fails. To pass, the final mark must be 50 or more.

14. For example, given the data 20 10 15 56, the final mark is calculated by

15. (20+10+15)/75*50 + 56/100*50 = 58

16. Write a program to request two times given in 24-hour clock format and find the time (in hours and minutes) that has elapsed between the first time and the second time. You may assume that the second time is later than the first time. Each time is represented by two numbers: e.g. 16 45 means the time 16:45, that is 4:45 p.m.

17. For example, if the two given times are 16 45 and 23 25 your answer should be 6 hours 40 minutes.

18. Modify the program so that it works as follows: if the second time is sooner than the first time, take it to mean a time for the next day. For example, given the times 20:30 and 6:15, take this to mean 8.30 p.m. to 6.15 a.m. of the next day. Your answer should be 9 hours 45 minutes.

19. A bank pays interest based on the amount of money deposited. If the amount is less than $5,000, the interest is 4% per annum. If the amount is $5,000 or more but less than $10,000, the interest is 5% per annum. If the amount is $10,000 or more but less than $20,000, the interest is 6% per annum. If the amount is $20,000 or more, the interest is 7% per annum.

20. Write a program to request the amount deposited and print the interest earned for one year.

21. For any year between 1900 and 2099, inclusive, the month and day on which Easter Sunday falls can be determined by the following algorithm:

22. set a to year minus 1900

23. set b to the remainder when a is divided by 19

24. set c to the integer quotient when 7b + 1 is divided by 19

25. set d to the remainder when 11b + 4 - c is divided by 29

26. set e to the integer quotient when a is divided by 4

27. set f to the remainder when a + e + 31 - d is divided by 7

28. set g to 25 minus the sum of d and f

29. if g is less than or equal to 0 then

30. set month to ‘March’

31. set day to 31 + g

32. else

33. set month to ‘April’

34. set day to g

35. endif

36. Write a program which requests a year between 1900 and 2099, inclusive, and checks if the year is valid. If it is, print the day on which Easter Sunday falls in that year. For example, if the year is 1999, your program should print April 4.

37. Write a program to prompt for the name of an item, its previous price and its current price. Print the percentage increase or decrease in the price. For example, if the previous price is $80 and the current price is $100, you should print increase of 25%; if the previous price is $100 and the current price is $80, you should print decrease of 20%.

38. A country charges income tax as follows based on one’s gross salary. No tax is charged on the first 20% of salary. The remaining 80% is called taxable income. Tax is paid as follows:

§ 10% on the first $15,000 of taxable income;

§ 20% on the next $20,000 of taxable income;

§ 25% on all taxable income in excess of $35,000;

1. Write a program to read a value for a person’s salary and print the amount of tax to be paid. Also print the average tax rate, that is, the percentage of salary that is paid in tax. For example, on a salary of $20,000, a person pays $1700 in tax.

2. The average tax rate is 1700/20000*100 = 8.5%.