10.1 define Statement

I l @ ve RuBoard

10.1 #define Statement

The #define statement can be used to define a constant. For example, the following two lines perform similar functions:

 #define SIZE 20       // The array size is 20 const int SIZE = 20;  // The array size is 20 

Actually the line #define SIZE 20 acts as a command to the preprocessor to globallychangeSIZEto20. This takes the drudgery and guesswork out of making changes.

All preprocessor commands begin with a hash mark (#) as the first character of the line. (You can put whitespace before the #, but this is rarely done.) C++ is free-format . Language elements can be placed anywhere on a line, and the end-of-line is treated just like a space. The preprocessor is not free-format. It depends on the hash mark (#) being the first character on the line. As you will see, the preprocessor knows nothing about C++ and can be (and is) used to edit things other than C++ programs.

The preprocessor is not part of the core C++ compiler. It uses an entirely different syntax and requires an entirely different mindset to use it well. Most problems you will see occur when the preprocessor is treated like C++.

Preprocessor directives terminate at the end of the line. In C++ a semicolon (;) ends a statement. The preprocessor directives do not end in a semicolon, and putting one in can lead to unexpected results. A preprocessor directive can be continued by putting a backslash (\) at the end of the line. The simplest use of the preprocessor is to define a replacement macro. For example, the command:

 #define FOO bar 

causes the preprocessor to replace the word "FOO" with the word "bar" everywhere "FOO" occurs. It is common programming practice to use all uppercase letters for macro names . This makes it very easy to tell the difference between a variable (all lowercase) and a macro (all uppercase).

The general form of a simple #define statement is:

 #define   Name Substitute-Text   

Name can be any valid C++ identifier. Substitute-Text can be anything as long as it fits on a single line. The Substitute-Text can include spaces, operators, and other characters .

Consider the following definition:

 #define FOR_ALL for (i = 0; i < ARRAY_SIZE; ++i) 

It is possible to use it like this:

 /*   * Clear the array   */  FOR_ALL {      data[i] = 0;  } 

It is considered bad programming practice to define macros in this manner. Doing so tends to obscure the basic control flow of the program. In this example, if the programmer wants to know what the loop does, he must search the beginning of the program for the definition of FOR_ALL .

It is even worse to define macros that do large-scale replacement of basic C++ programming constructs. For example, you can define the following:

 #define BEGIN {  #define END }  . . .      if (index == 0)       BEGIN          std::cout << "Starting\n";      END 

The problem is that you are no longer programming in C++, but in a half C++, half-PASCAL mongrel.

The preprocessor can cause unexpected problems because it does not check for correct C++ syntax. For example, Example 10-1 generates an error on line 11.

Example 10-1. big/big.cpp
 1:#define BIG_NUMBER 10 ** 10  2:  3:int main(  )  4:{  5:    // index for our calculations   6:   int   index;                  7:  8:    index = 0;  9: 10:    // syntax error on next line  11:   while (index < BIG_NUMBER) { 12:        index = index * 8; 13:    } 14:   return (0); 15: 

The problem is in the #define statement on line 1, but the error message points to line 11. The definition in line 1 causes the preprocessor to expand line 11 to look like:

 while (index < 10 ** 10) 

Because ** is an illegal operator, this generates a syntax error.

Question 10-1: The following program generates the answer 47 instead of the expected answer 144. Why? (Hint below.)

Example 10-2. first/first.cpp
 #include <iostream> #define FIRST_PART      7  #define LAST_PART       5  #define ALL_PARTS       FIRST_PART + LAST_PART  int main(  ) {      std::cout << "The square of all the parts is " <<          ALL_PARTS * ALL_PARTS << '\n';     return (0); } 

Hint:

 CC -E prog.cc 

sends the output of the preprocessor to the standard output.

In MS-DOS/Windows, the command:

 cpp prog.cpp 

creates a file called prog.i containing the output of the preprocessor.

Running the program for Example 10-2 through the preprocessor gives you the code shown in Example 10-3.

Example 10-3. first/first-ed.out
 # 1 "first.cpp" # 1 "/usr/local/lib/g++-include/iostream" 1 3   // About 900 lines of #include stuff omitted inline ios& oct(ios& i) { i.setf(ios::oct, ios::decios::hexios::oct); return i; } # 1 "first.cpp" 2 int main(  ) {      std::cout << "The square of all the parts is " <<                     7 + 5 * 7 + 5 << '\n';     return (0); } 

The output of the C++ preprocessor contains a lot of information, most of which can easily be ignored. In this case, you need to scan the output until you reach the std::cout line. Examining this line will give you an idea of what caused the error.

Question 10-2: Example 10-4 generates a warning that counter is used before it is set. This is a surprise because the for loop should set it. You also get a very strange warning, "null effect," for line 11. What's going on?

Example 10-4. max/max.cpp
 // warning, spacing is VERY important  #include <iostream> #define MAX =10 int main(  ) {     int  counter;     for (counter =MAX; counter > 0; --counter)         std::cout << "Hi there\n";     return (0); } 

Hint : Take a look at the preprocessor output.

Some preprocessors, such as the one that comes with the g++ compiler, add spaces around the tokens, which makes this program fail with a syntax error instead of compiling and generating strange code.

Question 10-3: Example 10-5 computes the wrong value for size . Why?

Example 10-5. size/size.cpp
 #include <iostream> #define SIZE    10; #define FUDGE   SIZE -2; int main(  ) {     int size;// size to really use           size = FUDGE;     std::cout << "Size is " << size << '\n';     return (0); } 

Question 10-4: Example 10-6 is supposed to print the message Fatal Error: Abort and exit when it receives bad data. But when it gets good data, it exits. Why?

Example 10-6. die/die.cpp
 #include <iostream> #include <cstdlib> #define DIE \     std::cerr << "Fatal Error:Abort\n";exit(8);  int main(  ) {          // a random value for testing      int value;            value = 1;      if (value < 0)          DIE;      std::cerr << "We did not die\n";     return (0); } 

10.1.1 #define Versus const

The const keyword is relatively new. Before const , #define was the only way to define constants, so most older code uses #define directives. However,the useof const is preferred over #define for several reasons. First, C++ checks the syntax of const statements immediately. The #define directive is not checked until the macro is used. Also, const uses C++ syntax, while #define has a syntax all its own. Finally, const follows normal C++ scope rules, whereas constantsdefined by a #define directive continue on forever.

In most cases a const statement is preferred over #define . Here are two ways of defining the same constant:

 #define MAX 10 // Define a value using the pre-processor                // (This can easily cause problems) const int MAX = 10; // Define a C++ constant integer                     // (Safer) 

The #define directive is limited to defining simple constants. The const statement can define almost any type of C++ constant, including things such as structure classes. For example:

 struct box {     int width, height;   // Dimensions of the box in pixels }; // Size of a pink box to be used for input const box pink_box(1, 4); 

The #define directive is, however, essential for things such as conditional compilation and other specialized uses.

I l @ ve RuBoard


Practical C++ Programming
Practical C Programming, 3rd Edition
ISBN: 1565923065
EAN: 2147483647
Year: 2003
Pages: 364

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