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

2.5.2 Simple Compiler Directives

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:

All compiler directives, including define, require a # as the first non-white space character in a line. (Some older compilers require that # be in the first column of a line but most modern compilers allow leading white space on a line before #). The semantics of this directive is to define a string of characters, <substitution_string>, which is to be substituted for every occurrence of the symbolic name, <symbol_name>, in the code for the remainder of the source file. Keep in mind, a directive is not a statement in C, nor is it terminated by a semi-colon; it is simply additional information given to the compiler.

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:



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

tep@wiliki.eng.hawaii.edu
Tue Aug 16 14:01:55 HST 1994