Certain language facilities are provided by means of a preprocessor, which is a separate first step in compilation.
File Inclusion
We could include contents of a file during compilation using #include.
#include "filename"
or
#include <filename>
If the filename is inside double quotes, then searching for the file typically begins in the directory containing the source program; if it is not found there or if the name is enclosed in < and >, searching follows an implementation-defined rule to find the file.
#include is the preferred way to tie the declarations together for a large program.
Macro Substitution
Macro substitution is used to replace a token name by an arbitrary sequence of characters.
#define name replacement_text
Each subsequent occurrence of name will be replaced by the replacement_text. The name has the same form as a variable name and the replacement_text is arbitrary. Normally, the replacement_text is the rest of the line, but a long definition may continue over several lines by placing a \ at the end of each line to be continued.
The scope of a name defined with #define is from the point of its definition ti the end of the source file being compiled. A definition may use previous definitions. Substitutions are only done for tokens, not within quoted strings. For example, if FOO is a defined name, there would be no substitution in printf(“FOO”) or in FOOBAR. Let’s look at a few examples:
#define CENTURY 100 #define MILLENNIUM 1000
We may also have macros with arguments, so the replacement text can be different for different calls of the macro.
#define min(A, B) ((A) < (B) ? (A) : (B))
Although it looks like a function call, a use of min expands to in-line code. So,
x = min(a + b, c + d);
will be replaced by the line
x = ((a + b) < (c + d) ? (a + b) : (c + d));
This would work for different data types as well. There are some common pitfalls of which we need to be aware. For instance, if we use the min macro in the following manner:
x = min(i--, j--);
This would be expanded to:
x = ((i--) < (j--) ? (i--) : (j--));
This would decrement the smaller value twice, which isn’t what we intended.
We also need to be careful about the parentheses. Let’s look at the following macro:
#define square(x) x * x
If we invoke this using:
z = square(z + 1);
This would be expanded to:
z = z + 1 * z + 1;
which is incorrect.
Macros are valuable, but we must be careful about the pitfalls when using them.
Conditional Inclusion
It is possible to control preprocessing itself with conditional statements that are evaluated during preprocessing. This provides a way to include code selectively, depending on the value of conditions evaluated during compilation.
The #if line evaluates a constant integer expression. If it is non-zero, then subsequent lines until #endif or #elif or #else are included. The expression defined(name) in a #if is 1 if the name is defined else 0.
#if SYSTEM == SYSV #define HDR "sysv.h" #elif SYSTEM == BSD #define HDR "bsd.h" #elif SYSTEM == MSDOS #define HDR "msdos.h" #else #define HDR "default.h" #endif #include HDR
In the above example, the name SYSTEM is used to decide which version of the header to include.
#ifdef and #ifndef are specialised forms that test whether a name is defined.