Constants and the C Preprocessor

I l @ ve RuBoard

Constants and the C Preprocessor

Sometimes you need to use a constant in a program. For example, you could give the circumference of a circle as this:

 circumference = 3.14159 * diameter; 

Here, the constant 3.14159 represents the world-famous constant pi ( ? ). To use a constant, just type in the actual value, as in the example. However, there are good reasons to use a symbolic constant instead. That is, you could use a statement like the following and have the computer substitute in the actual value later:

 circumference = pi * diameter; 

Why is it better to use a symbolic constant? First, a name tells you more than a number does. Compare these two statements:

 owed = 0.015 * housevalue; owed = taxrate * housevalue; 

If you read through a long program, the meaning of the second version is more plain than the first.

Second, suppose you have used a constant in several places, and it becomes necessary to change its value. After all, tax rates do change. Then you need merely to alter the definition of the symbolic constant, rather than find and change every occurrence of the constant in the program.

Okay, how do you set up a symbolic constant? One way is to declare a variable and set it equal to the desired constant. You could write this:

 float taxrate; taxrate = 0.015; 

This way is all right for a small program, but it is somewhat wasteful because the computer has to peek into the taxrate memory location every time taxrate is used. This is an example of execution time substitution because the substitution takes place while the program is running. Fortunately, C has a couple of better ideas.

The original better idea is the C preprocessor. In Chapter 2, "Introducing C," you saw how the preprocessor uses #include to incorporate information from another file. The preprocessor also lets you define constants. Just add a line like this at the top of the file containing your program:

 #define TAXRATE 0.015 

When your program is compiled, the value 0.015 will be substituted everywhere you have used TAXRATE . This is called a compile time substitution. By the time you run the program, all the substitutions have already been made (see Figure 4.5). Such defined constants are often termed manifest constants .

Figure 4.5. Whay you type versus what is compiled.
graphics/04fig05.jpg

Note the format. First comes #define . In older implementations , the # sign should be in the leftmost column; ANSI C removes this restriction. Next comes the symbolic name ( TAXRATE ) for the constant and then the value ( 0.015 ) for the constant. So the general form is as follows :

 #define NAME value 

You would substitute the symbolic name of your choice for NAME and the appropriate value for value . No semicolon is used because this is a substitution mechanism, not a C statement. Why is TAXRATE capitalized? It is a sensible C tradition to type constants in upper-case . Then, when you encounter one in the depths of a program, you know immediately that it is a constant, not a variable. Capitalizing constants is just another technique to make programs more readable. Your programs will still work if you don't capitalize the constants, but capitalizing them is a good habit to cultivate.

The names you use for symbolic constants must satisfy the same rules that the names of variables do. You can use uppercase and lowercase letters , digits, and the underscore character. The first character cannot be a digit. Listing 4.4 shows a simple example.

Listing 4.4 The pizza.c program.
 /* pizza.c -- uses defined constants in a pizza context */ #include <stdio.h> #define PI 3.14159 int main(void) {   float area, circum, radius;   printf("What is the radius of your pizza?\n");   scanf("%f", &radius);   area = PI * radius * radius;   circum = 2.0 * PI *radius;   printf("Your basic pizza parameters are as follows:\n");   printf("circumference = %1.2f, area = %1.2f\n", circum,          area);   return 0; } 

The %1.2f in the printf() statement causes the printout to be rounded to two decimal places. Of course, this program may not reflect your major pizza concerns, but it does fill a small niche in the world of pizza programs. Here is a sample run:

 What is the radius of your pizza?  6.0  Your basic pizza parameters are as follows: circumference = 37.70, area = 113.10 

The #define statement can be used for character and string constants, too. Just use single quotes for the former and double quotes for the latter. The following examples are valid:

 #define BEEP '\a' #define TEE 'T' #define ESC '3' #define OOPS "Now you have done it!" 

Remember that everything following the symbolic name is substituted for it. Don't make this common error:

 /* the following is wrong */ #define TOES = 20 

If you do this, TOES is replaced by = 20 , not just 20 . In that case, a statement like

 digits = fingers + TOES; 
is converted to the following misrepresentation:
 digits = fingers + = 20; 

The const Modifier

ANSI C adds a second way to create symbolic constants ”using the const keyword to modify a variable declaration:

 const int MONTHS = 12;    // MONTHS a symbolic constant for 12 

This makes MONTHS into a read-only value. That is, you can display MONTHS and use it in calculations, but you cannot alter the value of MONTHS . This newer approach is more flexible than using #define ; Chapter 13, "Storage Classes and Program Development," discusses this and other uses of const .

Actually, ANSI C has yet a third way to create symbolic constants, and that is the enum facility discussed in Chapter 16, "The C Preprocessor and the C Library."

Using #define and #include Together

Here is a special treat for the lazy. Suppose you develop a whole packet of programs that use the same set of constants. You can do the following:

  • Collect all your #define statements in one file; call it, say, const.h .

  • At the head of each source code file of your program, insert the following statement:

 #include "const.h" 

Then, when you run the program, the preprocessor will read the file const.h and use all the #define statements there for your program. Incidentally, the .h at the end of the filename is a reminder to you that the file is a header, that is, full of information to be placed at the head of your program. The preprocessor itself doesn't care whether you use a .h in the name.

Note that we used "const.h" , not <const.h> . The difference lies in where the compiler first looks for the include file, and it is implementation dependent. On UNIX systems, placing the filename in angle brackets causes the preprocessor to look in specific system directories for the files. Placing the filename in quotes causes the preprocessor to look in the current directory first and then in the system directories. Or you can specify a full pathname, as in "/usr/tiger/myincludes/bar.h" , in which case just the indicated directory ( /usr/tiger/myincludes ) is searched. Many IDEs have menu choices to select the standard include directory. Again, angle brackets mean to search only the standard include directory, and quotes mean to search the current directory first. Incidentally, although UNIX uses the slash ( / ) in pathnames, the DOS environment uses the backslash ( \ ) for the same purpose.

C, A Master of Disguise: Creating Aliases

The capabilities of #define go beyond the symbolic representation of constants. Consider, for instance, the program in Listing 4.5. (By the way, this example is meant to be a mildly entertaining illustration of how the preprocessor works; it most definitely is not intended to be a model to emulate.)

Listing 4.5 The alias.c program.
 /* alias.c -- uses preprocessor frivously */ #include <stdio.h> #include "alias.h"   /* see text for more on this file */ program   begin     whole yours, mine then     spitout("Give me an integer, please.\n") then     takein("%d", &yours) then     mine = yours times TWO then     spitout("%d is twice your number!\n", mine) then   end 

Hmmm, Listing 4.5 looks vaguely familiar, a little like Pascal, but it doesn't seem to be C. The secret, of course, is in the file alias.h . What's in it? Read on.

 /* alias.h -- a silly abuse of preprocessing power */ #define program int main(void) #define begin   { #define end     return 0;} #define then    ; #define takein  scanf #define spitout printf #define TWO     2 #define times   * #define whole   int 

This example illustrates how the preprocessor works. Your program is searched for items defined by #define statements, and all finds are then replaced. In this example, all then s are rendered into semicolons at compilation. The resulting program is identical to what you would have received by typing in the usual C terms at the start.

This powerful defining facility can also be used to define a macro, which is sort of a poor man's function. I will return to this topic in Chapter 16.

The #define feature has limitations. For example, parts of a program within double quotes are immune to substitution. The following combination wouldn't work:

 #define MN "minimifidianism" printf("He was a strong believer in MN.\n"); 

The printout would read simply:

 He was a strong believer in MN. 

This is because MN is enclosed in double quotes in the printf() statement. However, the statement

 printf("He was a strong believer in %s.\n", MN); 
would produce
 He was a strong believer in minimifidianism. 

In this case, the MN was outside the double quotes, so it was replaced by its definition. ( Minimifidianism , by the way, means having almost no belief.)

Manifest Constants on the Job

The ANSI C header files limits.h and float.h supply detailed information about the size limits of integer types and floating types, respectively. Each file defines a series of manifest constants that apply to your implementation. For instance, the limits.h file contains lines similar to the following:

 #define INT_MAX    +32767 #define INT_MIN    -32768 

These constants represent the largest and smallest possible values for the int type. If your system used a 32-bit int , the file would provide different values for these symbolic constants. The file defines minimum and maximum values for all the integer types. If you include the limits.h file, you can use code like the following:

 printf("Maximum int value on this system = %d\n", INT_MAX); 

If your system used a four-byte int , the limits.h file that came with that system would provide definitions for INT_MAX and INT_MIN that matched the limits of a four-byte int . Table 4.1 lists some of the constants found in limits.h .

Table  4.1. Some symbolic constants from limits.h .
Symbolic Constant Represents
CHAR_BIT Number of bits in a char
CHAR_MAX Maximum char value
CHAR_MIN Minimum char value
SCHAR_MAX Maximum signed char value
SCHAR_MIN Minimum signed char value
UCHAR_MAX Maximum unsigned char value
SHRT_MAX Maximum short value
SHRT_MIN Minimum short value
USHRT_MAX Maximum unsigned short value
INT_MAX Maximum int value
INT_MIN Minimum int value
UINT_MAX Maximum unsigned int value
LONG_MAX Maximum long value
LONG_MIN Minimum long value
ULONG_MAX Maximum unsigned long value

Similarly, the float.h file defines constants such as FLT_DIG and DBL_DIG , which represent the number of significant figures supported by the float type and the double type. Table 4.2 lists some of the constants found in float.h . This sample relates to the float type. Equivalent constants are defined for types double and long double, with DBL and LDBL substituted for FLT in the name. (The table assumes the system represents floating-point numbers in terms of powers of 2.)

Table  4.2. Some symbolic constants from limits.h .
Symbolic Constant Represents
FLT_MANT_DIG Number of bits in the mantissa of a float
FLT_DIG Minimum number of significant decimal digits for a float
FLT_MIN_10_EXP Minimum base-10 negative exponent for a float with full set of significant figures
FLT_MAX_10_EXP Maximum base-10 positive exponent for a float
FLT_MIN Minimum value for a positive float
FLT_MIN Maximum value for a positive float
FLT_EPSILON Difference between 1.00 and the least float value greater than 1. 00

The C preprocessor is a useful, helpful tool, so take advantage of it when you can. We'll show you more applications as you move along through this book.

I l @ ve RuBoard


C++ Primer Plus
C Primer Plus (5th Edition)
ISBN: 0672326965
EAN: 2147483647
Year: 2000
Pages: 314
Authors: Stephen Prata

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net