9.3 Conditional Compilation
[This section corresponds to K&R Sec. 4.11.3]
The last preprocessor directive we're going to look at is
#ifdef.
If you have the sequence
#ifdef name
program text
#else
more program text
#endif
in your program,
the code that gets compiled depends on whether a preprocessor macro by that
name is defined or not.
If it is
(that is, if there has been a #define line
for a macro called name),
then
``program text'' is compiled and
``more program text'' is ignored.
If the macro is not defined,
``more program text'' is compiled and
``program text'' is ignored.
This looks a lot like an if statement,
but it behaves completely differently:
an if statement controls which statements of your
program are executed at run time,
but #ifdef controls which parts of your program actually
get compiled.
Just as for the if statement, the #else in an
#ifdef is optional.
There is a companion directive #ifndef, which compiles
code if the macro is not defined
(although the ``#else clause''
of an #ifndef directive will then be compiled if the
macro is defined).
There is also an #if directive which compiles code
depending on whether a compile-time expression is true or false.
(The expressions which are allowed in an #if directive
are somewhat restricted,
however, so we won't talk much
about #if here.)
Conditional compilation is useful in two general classes of
situations:
- You are trying to write a portable program,
but the way you do something is different depending on what
compiler, operating system, or computer you're using.
You place different versions of your code,
one for each situation,
between suitable #ifdef directives,
and when you compile the program in a particular environment,
you arrange to have the macro names defined which select the variants you
need in that environment.
(For this reason, compilers usually have ways of letting you
define macros from the invocation command line or in a
configuration file,
and many also predefine certain macro names related to
the operating system, processor, or compiler in use.
That way, you don't have to change the code to change the
#define lines each time you compile it in a different
environment.)
For example, in ANSI C, the function to delete a file is remove.
On older Unix systems, however, the function was called
unlink.
So if filename is a variable containing the name of a
file you want to delete,
and if you want to be able to compile the program under these
older Unix systems, you might write
#ifdef unix
unlink(filename);
#else
remove(filename);
#endif
Then, you could place the line
#define unix
at the top of the file when compiling under an old Unix system.
(Since all you're using the macro unix for is to control
the #ifdef,
you don't need to give it any replacement text at all.
Any definition for a macro,
even if the replacement text is empty,
causes an #ifdef to succeed.)
(In fact, in this example,
you wouldn't even need to define the macro unix at all,
because C compilers on old Unix systems tend to predefine it for you,
precisely so you can make tests like these.)
- You want to compile several different versions of your program,
with different features present in the different versions.
You bracket the code for each feature with #ifdef
directives,
and (as for the previous case)
arrange to have the right macros defined or not to build the
version you want to build
at any given time.
This way, you can build the several different versions from the
same source code.
(One common example is whether you turn debugging statements on
or off.
You can bracket each debugging printout with
#ifdef DEBUG and #endif,
and then turn on debugging only when you need it.)
For example, you might use lines like this:
#ifdef DEBUG
printf("x is %d\n", x);
#endif
to print out the value of the variable x at some point in
your program to see if it's what you expect.
To enable debugging printouts, you insert the line
#define DEBUG
at the top of the file,
and to turn them off, you delete that line,
but the debugging printouts quietly remain in your code,
temporarily deactivated,
but ready to reactivate if you find yourself needing them again later.
(Also, instead of inserting and deleting the #define line,
you might use a compiler flag such as -DDEBUG to define
the macro DEBUG from the compiler invocation
line.)
Conditional compilation can be very handy, but it can also get
out of hand.
When large chunks of the program are completely different depending on,
say, what operating system the program is being compiled for,
it's often better to place the different versions in separate
source files,
and then only use one of the files
(corresponding to one of the versions)
to build the program on any given system.
Also, if you are using an ANSI Standard compiler and you are
writing ANSI-compatible code,
you usually won't need so much conditional compilation,
because the Standard specifies exactly how the compiler must do certain things,
and exactly which library functions it must provide,
so you don't have to work so hard to accommodate the old
variations among compilers and libraries.
Read sequentially:
prev
next
up
top
This page by Steve Summit
// Copyright 1995-1997
// mail feedback