Previous: 2.5.1 Making Decisions with Branches
Up: 2.5 More C Statements
Next: 2.5.3 More on Expressions
Previous Page: 2.5.1 Making Decisions with Branches
Next Page: 2.5.3 More on Expressions
In some of the improvements we have made so far to our program for PAY2, we have used numeric constants in the statements themselves. For example, in the code:
if (hours_worked > 40.0) {
regular_pay = 40.0 * rate_of_pay;
overtime_pay = 1.5 * rate_of_pay * (hours_worked - 40.0);
}
else {
regular_pay = hours_worked * rate_of_pay;
overtime_pay = 0.0;
}
pay = regular_pay + overtime_pay;
we use the constant 40.0 as the limit on the number of regular pay hours
(hours beyond this are considered overtime),
and the constant 1.5 as the overtime pay rate (time and a half).
Use of numeric constants (sometimes called ``magic numbers'')
in program code is often considered bad style
because the practice makes the program logic harder to understand and debug.
In addition, the practice makes programs less flexible,
since making a change in the
values of numeric constants requires that the entire code be reviewed
to find all instances where the ``magic number'' is used.
C, like many other programming languages, allows the use of symbolic names for constants in programs. This facility makes use of the C preprocessor and takes the form of compiler directives. Compiler directives are not, strictly speaking, part of the source code of a program, but rather are special directions given to the compiler about how to compile the program. The directive we will use here, the define directive, has syntax:
In our case, we might use the following compiler directives to give names to our numeric constants:
#define REG_LIMIT 40.0
#define OT_FACTOR 1.5
These directives define that wherever the string of
characters REG_LIMIT occurs
in the source file, it is to be replaced by the string of characters 40.0
and that the
string OT_FACTOR is to be replaced by 1.5.
With these definitions, it is
possible for us to use REG_LIMIT and
OT_FACTOR in the program statements
instead of numeric constants.
Thus our code would become:
if (hours_worked > REG_LIMIT) {
regular_pay = REG_LIMIT * rate_of_pay;
overtime_pay = OT_FACTOR * rate_of_pay * (hours_worked - REG_LIMIT);
}
else {
regular_pay = hours_worked * rate_of_pay;
overtime_pay = 0.0;
}
pay = regular_pay + overtime_pay;
The code is now more readable; it says in words exactly what we
mean by these statements.
Before compilation proper, the preprocessor replaces
the symbolic constants with strings that constitute actual constants;
the string of characters 40.0 for the string
REG_LIMIT and 1.5 for OT_FACTOR
throughout the source program code.
The rules for the symbol names in directives are the same as those for identifiers. A common practice used by many programmers is to use upper case for the symbolic names in order to distinguish them from variable names. Remember, define directives result in a literal substitution without any data type checking, or evaluation. It is the responsibility of the programmer to use defines correctly. The source code is compiled after the preprocessor performs the substitutions.
The implementation of the PAY2 algorithm incorporating the above defines and other improvements discussed so far is shown in Figure 2.9.

Note in the code, when the hours worked do not exceed REG_LIMIT, the overtime pay is set to zero. A constant zero value in a program code is not unreasonable when the logic is clear enough.
Here is a sample session from the resulting executable file: