In this file we'll look at some of the core features of the C preprocessor, which handles most of the # statements in C source code. These statements are processed, usually with the effect of adding or altering the source code before actual compilation takes place. To see what the code looks like after preprocessing but before compilation, compile with the -E flag. The resulting file will be the post-processing C code. The core C preprocessor features are discussed below. (Discussion of more advanced macro expansion will be handled separately.) including the content of other files ==================================== #include // include a file from the predefined library collection #include "path/filename" // include a file created locally simple pattern substitution =========================== this is used to get the compiler to substitute one text pattern for another just prior to compilation #define ORIGINAL REPLACEMENT e.g. #define STRWIDTH 10 the STRWIDTH's below would be replaced with a 10: scanf("%STRWIDTHs", someCharArray); printf("%STRWIDTHs", someCharArray); thus the compiled code would look like scanf("%10s", someCharArray); printf("%10s", someCharArray); it is possible to undefine a previously defined identifier, e.g. #undef FOO substitutions are done based upon whatever definitions are known at that point, e.g. int foo = 3; // this foo is unaltered since it preceeds the #define #define foo abc int foo = 4; // this foo is changed to abc don't add // style comments within your macros #define yes 1 // treat yes and no like boolean values #define no 0 if (yes) { printf("ok"); } // since there are comments in the yes #define, the code above would translate into // the snippet below, effectively commenting out the ") {" if (1 // treat yes and no like boolean values) { printf("ok"); } note that the /* .... */ style of comment is ok inside a macro conditional compilation: ======================== two main forms of conditional compilation are supported: (1) conditional compilation based on the value of an expression #if EXPRESSION1 code to include if expression 1 is true #elif EXPRESSION2 code to include if expression 2 is true (but 1 is false) #else code to include if neither expression is true #endif For example, we might define DEBUG_LEVEL with a variety of possible values indicating how much debugging code should be included at compile time: #define DEBUG_LEVEL 1 Then in the code we could have different extra elements included based on the chosen debug level, e.g. #elif DEBUG_LEVEL == 1 printf(" some kind of basic debug info "); #elif DEBUG_LEVEL == 2 printf(" more detailed debug info "); #endif (note that if the debug level is anything else then no debug code would get included) (2) conditional compilation based on whether or not some identifier has been previously defined #ifdef IDENTIFIER code to include if the identifier has been previously defined #endif For example, we might include different libraries if compiled on a windows system than on linux or unix or mac, so we rely on identifiers that are commonly included by compilers on those systems, e.g. #ifdef __unix__ the libraries necessary on a unix system #elif defined _WIN32 the libraries necessary on a windows system #endif note there is also a if-not-defined #ifndef IDENTIFIER code to include if the identifier has not been previously defined #endif This is often used together with a #define to ensure that a block of code (e.g. in a header file) is never included more than once, e.g. #ifndef SOMEIDENTIFIERWEPICK #define SOMEIDENTIFIERWEPICK the code we want included once only #endif special compiler directives =========================== pre-defined substitutions __FILE__ will be replaced with the current filename __LINE__ will be replaced with the current line number __DATE__ will be replaced by the current date __TIME__ will be replaced by the current time __VERSION__ will be replaced by the current C version number etc. - there are a great many others (compiler dependent) __VA_ARGS__ used with ... will be replaced by any sequence of arguments (see the macro expansion section below) special purpose substitutions // turn off assert checking in the program #define NDEBUG converting text segments to a double-quoted string, escaped appropriately e.g. to convert p = "hi there\n" to "p = \"hi there\\n\"" str( p = "hi there\n";) a similar effect can be achieved with a single identifier by using the #, e.g. // convert x to "x" #define text(x) #x display error and warning messages #error "blah blah blah" #warning "blah blah blah" The ability to created new identifiers is sometimes handy, for which the string concatenation operator ## is provided, e.g. #define concat(a,b) a ## b int x = concat(10,20); // x is 1020 (see the macro expansion section below) macro definition and expansion ============================== earlier we showed the #define used for simple text substitution, but it can be used for more complex parameterized code rewrites: First, define the pattern to be replaced and the code block to replace it with, e.g. suppose we want to be able to write source code like intswap(a,b), and have it automatically replaced with int tmp = a; a = b; b = tmp; a suitable substitution macro would be #define intswap(x,y) { int tmp = x; x = y; y = tmp; } Then use the pattern as desired in the code, e.g. int a = 102; int b = 217; intswap(a,b); // becomes { int tmp = a; a = b; b = tmp; } Problems: the macro shown assumes the variable tmp isn't previously declared in the current scope, and that the x and y used are int variables. If the programmer doesn't follow those assumptions then there will be compile-time errors. Other issues can also arise with side-effects, e.g. if the programmer tries things like intswap(a++,b++), which would expand to { int tmp = a++; a++ = b++; b++ = tmp; } Multi-line substitutions can be done in a #define using the \ line continuation char, e.g. #define intswap(x,y) { \ int tmp = x; \ x = y; \ y = tmp; \ } __VA_ARGS__ can be used together with ... to substitute for a variable number of arguments in a macro expansion, e.g. #define F(...) dosomething(__VA_ARGS__); (this will be used extensively in the more advanced macro operations discussed later)