C Preprocessor & Macro
|
||||||||||||||||
Introduction
|
||||||||||||||||
The C preprocessor is
a tool which filters your source code before it is compiled.
|
||||||||||||||||
The preprocessor allows
constants to be named using the # notation.
|
||||||||||||||||
The preprocessor provides
several other facilities which will be described here.
|
||||||||||||||||
It is particularly useful for selecting
machine dependent pieces of code for different computer types, allowing a
single program to be compiled and run on several different computers.
|
||||||||||||||||
The C preprocessor isn't
restricted to use with C programs only. Programmers who use other languages
may also find it useful, however it is tuned to recognize features of the C
language likecomments and strings, so its use may be
restricted in other circumstances.
|
||||||||||||||||
Set of commonly used Preprocessor
Directives and their Functions:
|
||||||||||||||||
|
||||||||||||||||
These Preprocessor or
directives can be divided into three categories:
|
||||||||||||||||
1. Macro Substitution Directives
|
||||||||||||||||
2. File Inclusion Directives
|
||||||||||||||||
3. Compiler Control Directives
|
Macro substitution directives
|
We have already met this facility, in
its simplest form it allows us to define textual substitutions using #define statement.
|
The #define statement
can be used for more, however, than simply defining symbolic constants.
|
In particular, it can be used to
define macros; its, single identifiers that are equivalent to expressions,
complete statement or groups of statements. Macros resemble function in this
sense.
|
They are defined in an altogether
different manner than functions, however, and they are treated differently
during the compilation process.
|
Format: #define identifier string
|
e.g.: #define MAXSIZE 256
|
This will lead to the value 256 being
substituted for each occurrence of the word MAXSIZE in the
file.
|
Printf("\narea =%d", length
*width);
|
Note that the format string " \n area
=%d" is unaffected by the #define statement.
|
When the program is executed, the
values for length and width are entered interactively from the keyboard, and
the corresponding value for area is displayed.
|
A typical interactive session is
shown below. The user's responses are underlined, as usual.
|
Length =_3
|
Width =_4
|
Area=12
|
Macro definitions are customarily
placed at the beginning of a file, ahead of the first function definition.
|
The scope of a macro definition
extends from its point of definition to the end of the file. However, a macro
defined in one file is not recognized within another file.
|
Multilane macros can be defined by
placing a backward slash (\) the end of each line except the
last.
|
This feature permits a single macro
(i.e. a single identifier) to represent a compound statement.
|
Macros are sometimes used in place of
functions within a program.
|
The use of a macro in place of a
function eliminates the time delays associated with function calls.
|
If a program contains many reported function
calls, the time savings resulting from the use of macros can become
significant.
|
On the other hand, macro substitution
will take place whenever a reference to a macro appears within a program.
|
Thus, a program that contains several
references to the same macro may become unreasonably long. We therefore face
a tradeoff between execution speed and size of the compiled object program.
|
The use of a macro is most
advantageous in applications where there are relatively few functions calls
but the function is called repeatedly.
|
Using #define to
Create Functional Macros
|
#define can also be given arguments which are used
in its replacement. The definitions are then called macros.
|
Macros work rather like functions, but with the
following minor differences.
|
Since macros are
implemented as a textual substitution, there is no effect on program
performance (as with functions).
|
Recursive macros are generally not a good idea.
|
Macros don't care about the type of
their arguments. Hence macros are a good choice where we might want to
operate on reals, integers or a mixture of the two.
|
Macros are full of traps for the
unwary programmer. In particular the textual substitution means that
arithmetic expressions are liable to be corrupted by the order of evaluation
rules.
|
Here is an example of a macro which
won't work:
|
#define DOUBLE(x) x+x
|
Now if we have a statement
|
a = DOUBLE(b) * c;
|
This will be expanded to
|
a = b+b * c;
|
And since * has a higher priority
than +, the compiler will treat it as.
|
a = b + (b * c);
|
The problem can be solved using a
more robust definition of DOUBLE
|
#define DOUBLE(x) (x+x)
|
Here the brackets around the
definition force the expression to be evaluated before any surrounding
operators are applied. This should make the macro more reliable.
|
In general it is better to write a C
function than risk using a macro
|
File inclusion
|
The preprocessor directive #include is
an instruction to read in the entire contents of another file at that point.
|
This is generally used to read in
header files for library functions. Header files contain details of functions
and types used within the library.
|
It must be included before the
program can make use of the library functions.
|
Library header file names are
enclosed in angle brackets, < >. These tell the preprocessor to
look for the header file in the standard location for library definitions.
|
Example: #include
|
another use for #include for
the programmer is where multi-file programs are being written.
|
Certain information is required at
the beginning of each program file. This can be put into a file called globals.h and
included in each program file.
|
Local header file names are usually
enclosed by double quotes, " ". It is conventional
to give header files a name which ends in .h to distinguish
them from other types of file.
|
Our globals.h file would be included
by the following line. #include "globals.h"
|
Compiler control directive
|
The C Preprocessor offer a feature
known as conditional compilation, which can be used to switch on or off a
particular line or group of lines in a program.
|
This is achieved by the
inserting #ifdef or #endif.
|
Conditional selection of code
using #ifdef,#endif.
|
The preprocessor has a conditional
statement similar to' C's if else.
|
It can be used to selectively include
statements in a program. This is often used where two different computer
types implement a feature in different ways. It allows the programmer to
produce a program which will run on either type.
|
The keywords for conditional
selection are; #ifdef, #else and #endif. #ifdef
|
takes a name as an argument, and
returns true if the name has a current definition. The name may be defined
using a #define, the -d option of the compiler.
|
#else
|
is optional and ends the block
beginning with #ifdef. It is used to create a 2 way optional selection.
|
#endif
|
ends the block started by #ifdef or #else.
|
Where the #ifdef is
true, statements between it and a following #else or #endif are
included in the program.
|
Where it is false, and there is a
following #else, statements between the #else and
the following#endif are included.
|
This is best illustrated by an
example.
|
Using #ifdef for
Different Computer Types
|
Conditional selection is rarely
performed using #defined values.
|
Note: sun is defined automatically on SUN computers.
vax is defined automatically on VAX computers. ibm is defined automatically
on IBM pc's else type not defined message will be displayed (different types
of computer)
|
Using #ifdef to Temporarily
Remove Program Statements
|
#ifdef also provides a useful means of temporarily
"blanking out" lines of a program.
|
The lines in question are preceded
by #ifdef NEVER and followed by #endif. Of
course you should ensure that the name NEVER isn't defined
anywhere.
|