The C preprocessor
What is preprocessor language, and what is it for?
The preprocessor is an independent language executed before the compilation of a program, regardless of the language of this one. It is used to define macro operations. Throughout this article, we will discuss the utilities of the C preprocessor.
The C preprocessor is a lexical preprocessor : it performs a lexical analysis of the code, content to substitute strings of characters by other pieces of code. This makes it possible, for example, to define macros by substitution.
When passing the preprocessor, only directives introduced by the # character are read . Thus, the lines intended for it will be executed even if syntax errors are present in the rest of the program.
At the end of the preprocessing, the code in C will be compiled.
Substitutions
A great strength of the C preprocessor is the ability to define substitutions using the #define directive . The source code is found directly modified, before compilation.
Suppose we want to specify the version number of the program inside it. We could define a “VERSION” variable that we would call as many times as needed.
Definition of a VERSION variable, which must be evaluated each time it is used in the code.
Or we can substitute the word VERSION, directly, with the value 1.38, throughout the program, using this directive to the preprocessor:
Substitution directive addressed to the preprocessor.
Thus, after passing the preprocessor, the printf command would become:
Note: in C ++, defining the VERSION variable as a constant makes it possible not to use #define, and to optimize, similarly, the code.
The substitution does not stop there: it is quite possible to substitute expressions!
Definition of functions in macro. Be careful to secure the expression with parentheses, so as not to cause a problem in the event of linear combinations in inputs, and other accidents.
These are called macros .
What is interesting in this technique is that the expressions are no longer called, but rather directly substituted in the code. Thus, at the time of execution, there will be no function call to be made at the level of the processor: one thus avoids many line breaks, etc … It is also entirely possible to substitute a word by a code block. It is obvious, however, that it takes up more code space.
Including files
An essential substitution is the inclusion of files. The #include directive effectively allows the contents of another file to be included , as if it had been written instead of this instruction.
Typically, declarations of module types and macros are placed in these libraries. For example, an extremely popular library, including basic C functions and macros, is the stdio.h library (for “Standard Input / Output Header”). We can therefore include it in the code using the following directive:
Inclusion of the stdio.h library in the program.
Conditional compilation
The preprocessor directives can be controlled by reinforcing #ifdef . This allows you to check if a macro has been defined upstream.
We have the possibility to use #else in case the POLYNOME macro is not defined. Be careful to close this structure with a #endif .
It is also possible to tell the compiler to compile only certain parts of the code and to pass some of it .
This can be used for debugging purposes, for example, or to define different codes depending on the operating system, the time (through the predefined constant __TIME__
), ...
Error and warning messages
It sometimes happens that options chosen by the user of a program cause it to fail. To avoid this, the programmer can include error messages and alerts in his code .
If a case is found to be critical and the program cannot function properly, it may indicate an error. This stops compilation by displaying a custom message.
Definition of a classic error: compilation will stop and the program will deliver this message to the user.
If it is not necessary to stop the compilation here, one can simply pass a custom warning message.
Here, the compilation will resume its course after displaying this warning message.
In conclusion, mastering these macro mechanics can be an interesting challenge for any programmer, in order to simplify a code as much as possible or to master its execution, for the purposes of clarity or debugging.