Wednesday, November 2, 2011

Macro Expansion of the The C Preprocessor in C programming language

Have a look at the following program.
#define UPPER 25
main( )
{
int i ;
for ( i = 1 ; i <= UPPER ; i++ )
printf ( "\n%d", i ) ;
}
In this program instead of writing 25 in the for loop we are writing it in the form of UPPER, which has already been defined before main( ) through the statement,
#define UPPER 25
This statement is called ‘macro definition’ or more commonly, just a ‘macro’. What purpose does it serve? During preprocessing, the preprocessor replaces every occurrence of UPPER in the program with 25. Here is another example of macro definition.
#define PI 3.1415
main( )
{
float r = 6.25 ;
float area ;
area = PI * r * r ;
printf ( "\nArea of circle = %f", area ) ;
}

UPPER and PI in the above programs are often called ‘macro templates’, whereas, 25 and 3.1415 are called their corresponding ‘macro expansions’.
When we compile the program, before the source code passes to the compiler it is examined by the C preprocessor for any macro definitions. When it sees the #define directive, it goes through the entire program in search of the macro templates; wherever it finds one, it replaces the macro template with the appropriate macro expansion. Only after this procedure has been completed is the program handed over to the compiler.
In C programming it is customary to use capital letters for macro template. This makes it easy for programmers to pick out all the macro templates when reading through the program.
Note that a macro template and its macro expansion are separated by blanks or tabs. A space between # and define is optional. Remember that a macro definition is never to be terminated by a semicolon.
And now a million dollar question... why use #define in the above programs? What have we gained by substituting PI for 3.1415 in our program? Probably, we have made the program easier to read. Even though 3.1415 is such a common constant that it is easily recognizable, there are many instances where a constant doesn’t reveal its purpose so readily. For example, if the phrase “\x1B[2J” causes the screen to clear. But which would you find easier to understand in the middle of your program “\x1B[2J” or “CLEARSCREEN”? Thus, we would use the macro definition
#define CLEARSCREEN "\x1B[2J"
Then wherever CLEARSCREEN appears in the program it would automatically be replaced by “\x1B[2J” before compilation begins There is perhaps a more important reason for using macro definition than mere readability. Suppose a constant like 3.1415 appears many times in your program. This value may have to be changed some day to 3.141592. Ordinarily, you would need to go through the program and manually change each occurrence of the constant. However, if you have defined PI in a #define directive, you only need to make one change, in the #define directive itself:
#define PI 3.141592
Beyond this the change will be made automatically to all occurrences of PI before the beginning of compilation.

In short, it is nice to know that you would be able to change values of a constant at all the places in the program by just making a change in the #define directive. This convenience may not matter for small programs shown above, but with large programs macro definitions are almost indispensable.
But the same purpose could have been served had we used a variable pi instead of a macro template PI. A variable could also have provided a meaningful name for a constant and permitted one change to effect many occurrences of the constant. It’s true that a variable can be used in this way. Then, why not use it? For three reasons it’s a bad idea.

Firstly, it is inefficient, since the compiler can generate faster and more compact code for constants than it can for variables. Secondly, using a variable for what is really a constant encourages sloppy thinking and makes the program more difficult to understand: if something never changes, it is hard to imagine it as a variable. And thirdly, there is always a danger that the variable may inadvertently get altered somewhere in the program. So it’s no longer a constant that you think it is.
Thus, using #define can produce more efficient and more easily understandable programs. This directive is used extensively by C programmers, as you will see in many programs in this book.
Following three examples show places where a #define directive is popularly used by C programmers.
A #define directive is many a times used to define operators as shown below.
#define AND &&
#define OR ||
main( )
{
int f = 1, x = 4, y = 90 ;
if ( ( f < 5 ) AND ( x <= 20 OR y <= 45 ) )
printf ( "\nYour PC will always work fine..." ) ;
else
printf ( "\nIn front of the maintenance man" ) ;
}
A #define directive could be used even to replace a condition, as shown below.
#define AND &&
#define ARANGE ( a > 25 AND a < 50 )
main( )
{
int a = 30 ;
if ( ARANGE )
printf ( "within range" ) ;
else
printf ( "out of range" ) ;
}

A #define directive could be used to replace even an entire C statement. This is shown below.
#define FOUND printf ( "The Yankee Doodle Virus" ) ;
main( )
{
char signature ;
if ( signature == 'Y' )
FOUND
else
printf ( "Safe... as yet !" ) ;
}

No comments:

Post a Comment