The C Preprocessor
How to use the C Preprocessor
The preprocessor is a tool that helps us a lot when programming with C. It is part of the C Standard, just like the language, the compiler and the standard library.
It parses our program and makes sure that the compiler gets all the things it needs before going on with the process.
What does it do, in practice?
For example, it looks up all the header files you include with the #include
directive.
It also looks at every constant you defined using #define
and substitutes it with its actual value.
That’s just the start, and I mentioned those 2 operations because they are the most common ones. The preprocessor can do a lot more.
Did you notice #include
and #define
have a #
at the beginning? That’s common to all the preprocessor directives. If a line starts with #
, that’s taken care by the preprocessor.
Conditionals
One of the things we can do is to use conditionals to change how our program will be compiled, depending on the value of an expression.
For example we can check if the DEBUG
constant is 0:
#include <stdio.h>
const int DEBUG = 0;
int main(void) {
#if DEBUG == 0
printf("I am NOT debugging\n");
#else
printf("I am debugging\n");
#endif
}
Symbolic constants
We can define a symbolic constant:
#define VALUE 1
#define PI 3.14
#define NAME "Flavio"
When we use NAME or PI or VALUE in our program, the preprocessor replaces its name with the value, before executing the program.
Symbolic constants are very useful because we can give names to values without creating variables at compilation time.
Macros
With #define
we can also define a macro. The difference between a macro and a symbolic constant is that a macro can accept an argument and typically contains code, while a symbolic constant is a value:
#define POWER(x) ((x) * (x))
Notice the parentheses around the arguments, a good practice to avoid issues when the macro is replaced in the precompilation process.
Then we can use it in our code like this:
printf("%u\n", POWER(4)); //16
The big difference with functions is that macros do not specify the type of their arguments or return values, which might be handy in some cases.
If defined
We can check if a symbolic constant or a macro is defined using #ifdef
:
#include <stdio.h>
#define VALUE 1
int main(void) {
#ifdef VALUE
printf("Value is defined\n");
#else
printf("Value is not defined\n");
#endif
}
We also have #ifndef
to check for the opposite (macro not defined).
We can also use #if defined
and #if !defined
to do the same task.
It’s common to wrap some block of code into a block like this:
#if 0
#endif
to temporarily prevent it to run, or to use a DEBUG symbolic constant:
#define DEBUG 0
#if DEBUG
//code only sent to the compiler
//if DEBUG is not 0
#endif
Predefined symbolic constants you can use
The preprocessor also defines a number of symbolic constants you can use, identified by the 2 underscores before and after the name, including:
__LINE__
translates to the current line in the source code file__FILE__
translates to the name of the file__DATE__
translates to the compilation date, in theMmm gg aaaa
format__TIME__
translates to the compilation time, in thehh:mm:ss
format
→ I wrote 17 books to help you become a better developer, download them all at $0 cost by joining my newsletter
→ JOIN MY CODING BOOTCAMP, an amazing cohort course that will be a huge step up in your coding career - covering React, Next.js - next edition February 2025