Do not ignore C compiler preprocessor
You maybe don't know or probably didn't think about this but you are using program preprocessing before compiling it. As I said before compiling I mean, that each time you are compiling your project, C compiler prepares program file to be ready for compilation. Preprocessor includes other files to main file, defines symbol constants and macros, prepares conditional compilation of code. All preprocessor tasks are marked with ampersand symbol “#”. Lets go through most of directives of preprocessor:
One and common directive is #include. You have already noticed, that you use this directive to include external c files and headers to your project. You can use #include in two ways:
The difference between them is where preprocessor will be looking for these files. If “< >” are used then preprocessor will be looking for the standard library if “” ”” then preprocessor will look in default project folder or location defined in the makefile.
#define is a second important directive which allows creation of constants and macro commands. Structure of usage define is:
#define identifier value
When constant or macro command is defined, then in any place where Identifier is used it will be changed to defined value. For instance
#define PI 3.14159
Then in program PI value will be replaced by 3.14159. This allows to control values very easily. It could be some particular constants, port address definitions. And of course usage of good nomenclature of identifiers makes program more readable and descriptive.
#define can also be used to define macro commands. Following example will clear things out:
#define CIRCLE_AREA(x) (PI*(x)*(x))
Each time when in program CIRCLE_AREA(x) will be used, identifier will be replaced with its value like:
will be replaced by:
What is benefit of this? First of all when regular functions are used, then program calls them from specific address. Using macro commands functions are inserted in-line. Sometimes it may speed up program.
Opposite to #define directive is #undef directive which removes previous definition of constant or macro.
When you want to make your program more universal, then you can make perform conditional compilation. For instance if you want your program work with with multiple AVR mocrocontrollers then you cannot avoid conditional compilation depending on which processor is used. Simple example helps you understand the matter:
#define NULL 0
This is explained as follows. If NULL isn't define then define it. It depends on particular compiler but usually there are short variants of such commands. For instance: #ifdef – stands for (#if defined) and #ifndef – stands for (#if !defined)or #elif – (else if). Conditional definitions may be powerful tool in debugging process. For instance you may define:
#define DEBUG 1
Then in program you can add following lines in any place which can be turned on and off by changing DEBUG identifier value:
line printf() will be compiled if DEBUG identifier will have value 1 and line will be skipped if value will be 0.
The command #error causes the preprocessor to report a fatal error. The rest of the line that follows #error is used as the error message.
You would use #error inside of a conditional that detects a combination of parameters which you know the program does not properly support.
#error debug mode doesn't work here
The command #warning causes the preprocessor to report a warning but doesn't stop compilation like error command.
For more preprocessor commands and and usage refer to GCC documentation.