Suite101

Conditional Compilation in C

Platform-specific constraints, debug builds and performance issue clarified by the GNU Foundation.

© Guy Lecky-Thompson

The Right Target, sxc.hu
Conditional compilation in C is often slightly misunderstood, and we try to clarify the theory, practice, and considered good practices, courtesy of the GNU foundation.

The general idea behind conditional compilation is that a piece of code can be selectively compiled, depending upon whether a specific value has been #defined, or not. Reasons for doing this vary, but the main ones seem to be:

  • Platform specific constraints
  • Debug builds
  • Performance issues

(When we talk of 'platform specific' here, we mean flavors of the same platform, rather than different operating systems or hardware platforms.)

In essence, the aim is to create a different executable file (application), depending on several flags that are set by the programmer. Although, as we shall see, the technique is also used to prevent multiple #includes, which generate errors during the compile process.

Conditional Compilation with Pre-Processor Directives

A pre-processor directive in C is identified by the presence of a #symbol before the statement. For those not familiar with the concept, we can define macros (which may be a single value, or an actual executable code segment) which can be evaluated as if they are pieces of code. They are, however, processed before the main bulk of the compilation is performed.

Essentially, this means that the pre-processor substitutes language code for all the directives, and passes the newly inflated file to the compiler. We would expect, using conditional compilation, that some code would be left out, but this is not always the case, as we shall see later on.

If we wanted, for example, to create a project with a debug build, by using conditional compilation to include statements for debug output, we would need to #define a debug macro at the start of the file, and then test for it before evaluating debug statements. If this technique spans multiple files, it would probably be useful to define a compiler flag -DDEBUG, rather than have to include the #define statement at the start of each file.

Once defined, we could then use this debug flag as follows:

#ifdef DEBUG

printf("x is %d\n", x);

#endif

If we were trying to resolve some platform dependencies, we could achieve this using an additional #else directive:

#ifdef WIN_16

// Win16 code

#else

// Win32 code

#endif

There will be many other instances where this technique is useful, such as preventing multiple #includes, but it is essential not to over use the technique. One specific instance where it is very useful is in providing a kind of 'scripting language' wrapper to the C code so that non-programmers might make use of it.

This technique is covered in a later article. For now, let us press on with one of the most popular uses for conditional compilation.

Preventing Multiple #includes using Conditional Compilation

This technique is reasonably frequently used, and is a good, safe, way to prevent a file being included unnecessarily and causing conflicts when the compiler passes over the pre-processed C code. These conflicts occur when the pre-processor #includes the same header file twice, and the compiler detects that there are duplicate definitions of certain constants, macros or functions.

To avoid this, the following code can be used:

#ifndef _HEADER_H_INC

#include "header.h"

#define _HEADER_H_INC

#endif

Note that the code must be present every time that the header.h file is to be included, or that the header.h file itself contains, as the first line the #ifndef directive, and as the last two lines, the #define and #endif directives. This will have the effect of conditionally compiling the entire file.

Best Practice

Firstly, if one is going to use conditional compilation, but finds that whole swathes of code end up being contained within vast networks of #ifdef ... #endif and if { ... } statements, perhaps using completely different source files would be a better option. Of course, this will also mean adjusting the makefile, or creating multiple makefiles, each compiling different sources, which was something that conditional compilation was supposed to solve.

So, perhaps we should limit conditional compilation to non-platform specific decisions. This would go some way to reducing overuse; but might result in a slight expansion of the project activities.

The GNU project states that it is far better to use the if construct with a #defined (0 or 1) value rather than an #ifdef ... #endif pre-processor directive. This would lead to code such as:

#define DEBUG_ON 1

// ... code statements

if (DEBUG_ON) {

// ... code statements

}

This might not always be practical, but since the GNU project tends to espouse best practice policies due to the diverse nature of their various teams and projects, the advice should be taken with the gravity that it deserves. A worthwhile aside is that the GCC compiler actually generates the same object code in each case.

While this is interesting, one cannot help wondering if this leads to code being unintentionally left in a project when the executable is built. An exercise for the reader is to find out - kudos to the first one to figure it out; enter answers in the discussion below, if you please.


The copyright of the article Conditional Compilation in C in Computer Programming is owned by Guy Lecky-Thompson. Permission to republish Conditional Compilation in C in print or online must be granted by the author in writing.





Post this Article to facebook Add this Article to del.icio.us! Digg this Article furl this Article Add this Article to Reddit Add this Article to Technorati Add this Article to Newsvine Add this Article to Windows Live Add this Article to Yahoo Add this Article to StumbleUpon Add this Article to BlinkLists Add this Article to Spurl Add this Article to Google Add this Article to Ask Add this Article to Squidoo