Common Uses of Preprocessor Directives in Embedded Software

Common Uses of Preprocessor Directives in Embedded Software

Many excellent codes utilize preprocessor directives to enhance their functionality. Today, we will discuss the content related to preprocessor directives.
The common preprocessing directives are as follows:
  1. # directive, has no effect
  2. #include includes a source code file
  3. #define defines a macro
  4. #undef undefines a macro
  5. #if if the given condition is true, compile the following code
  6. #ifdef if the macro is defined, compile the following code
  7. #ifndef if the macro is not defined, compile the following code
  8. #elif if the previous if condition is not true, and the current condition is true, compile the following code
  9. #endif ends an if...#else compilation block
  10. #error stops compilation and displays an error message

What are Preprocessor Directives?

Preprocessor directives are lines of code that start with the # symbol.
The # must be the first character of the line, excluding any whitespace characters. Following the # is the directive keyword, and any number of whitespace characters are allowed between the keyword and the # symbol.
The entire line constitutes a preprocessor directive, which will make certain transformations to the source code before the compiler compiles it.
Preprocessor directives are operations that occur before the compiler compiles the code, performing initial transformations to produce new source code for the compiler.
It can be seen that the preprocessing occurs prior to the compiler processing the source code.
In many programming languages, there is no inherent mechanism to accomplish the following functions: including other source files at compile time, defining macros, and deciding whether to include certain code at compile time based on conditions (to prevent multiple inclusions of certain files).
To accomplish these tasks, a preprocessor is needed. Although most compilers currently include a preprocessor, they are generally considered independent of the compiler.
The preprocessing process reads the source code, checks statements containing preprocessor directives and macro definitions, and performs corresponding transformations on the source code.
The preprocessing process also removes comments and unnecessary whitespace characters from the program.
#include includes a source code file
This preprocessor directive is probably the most commonly seen. Let’s briefly explain;
  1. The first method is to enclose the header file in angle brackets.
#include <stdio.h>
This format tells the preprocessor to search for the included header file in the compiler’s built-in or external library header files.
  1. The second method is to enclose the header file in double quotes.
#include "test.h"
This format tells the preprocessor to search for the included header file in the source code files of the currently compiled application, and if it cannot be found, then search in the compiler’s built-in header files.
The reason for using two different inclusion formats is that the compiler is installed in a common subdirectory, while the compiled application is in its own private subdirectory.
An application may contain both the public header files provided by the compiler and its own private header files.
Using two different inclusion formats allows the compiler to distinguish between a set of public header files among many header files.
#define defines a macro
The #define macro is widely used in C language, but due to some shortcomings of #define, C++ emphasizes using const to define constants.
A macro defines an identifier that represents specific content.
The preprocessing process replaces occurrences of the macro identifier in the source code with the value defined by the macro. Remember that it is merely a replacement of identifiers.
Below are some examples of using #define:
  1. Using #define to implement macros for finding maximum and minimum values
   #include <stdio.h>
   #define MAX(x,y) (((x)>(y))?(x):(y))
   #define MIN(x,y) (((x)<(y))?(x):(y))
   int main(void)
   {
   #ifdef MAX    // Check if this macro is defined
       printf("3 and 5 the max is:%d\n",MAX(3,5));
   #endif
   #ifdef MIN
       printf("3 and 5 the min is:%d\n",MIN(3,5));
   #endif
       return 0;
   }
   
   /*
    * (1) The ternary operator is more efficient than if-else.
    * (2) Be careful when using macros; parameters should be carefully enclosed in parentheses,
    * because macros are just simple text replacements and can lead to ambiguous errors if not handled properly.
   */
  1. Incorrect usage of macro definitions
#include <stdio.h>
#define SQR(x) (x*x)
int main(void)
{
   int b=3;
#ifdef SQR// Only the macro name is needed, no parameters required; parameters will cause warnings
   printf("a = %d\n",SQR(b+2));
#endif
   return 0;
}

/*
* First of all, this macro definition is incorrect. It does not calculate the square of B+2 in the program.
* During preprocessing, it is replaced with the following result: b+2*b+2
* The correct macro definition should be: #define SQR(x) ((x)*(x))
* Therefore, it is advisable to enclose parameters in parentheses to avoid ambiguity.
*/
  1. Connecting macro parameters
#include <stdio.h>
#define STR(s) #s
#define CONS(a,b) (int)(a##e##b)
int main(void)
{
#ifdef STR
   printf(STR(VCK));
#endif
#ifdef CONS
   printf("\n%d\n",CONS(2,3));
#endif
   return 0;
}

/* (Most of these are rarely used; if needed, refer to the manual)
* The first macro converts the parameter into a string using #.
* The second macro concatenates two macro parameters together using ##, resulting in aeb, 2e3 which means 2000.
*/
  1. Using macros to obtain the high or low byte of a word
#include <stdio.h>
#define WORD_LO(xxx) ((byte)((word)(xxx) & 255))
#define WORD_HI(xxx) ((byte)((word)(xxx) >> 8))
int main(void)
{
   return 0;
}

/*
* A word consists of 2 bytes; to get the low byte (the low 8 bits), perform a bitwise AND with 255 (0000,0000,1111,1111).
* To get the high byte (the high 8 bits), simply right shift by 8 bits.
*/
  1. Using macros to get the number of elements in an array
#include <stdio.h>
#define ARR_SIZE(a) (sizeof((a))/sizeof((a[0])))
int main(void)
{
   int array[100];
#ifdef ARR_SIZE
   printf("array has %d items.\n",ARR_SIZE(array));
#endif
   return 0;
}
/*
* Total size divided by the size of each type.
*/
Special caution should be exercised when using #define macros, especially when it involves calculations with parameters, as shown in the two examples below, the safest approach is to enclose parameters in parentheses.
Usage of #ifdef, #ifndef, #endif…
All the above precompiled directives are conditional compilation directives, which determine which code is compiled and which is not.
  1. Example 1:
#include <stdio.h>
#include <stdlib.h>
#define DEBUG
int main(void)
{
   int i = 0;
   char c;
   while(1)
   {
       i++;
       c = getchar();
       if('\n' != c)
       {
           getchar();
       }
       if('q' == c || 'Q' == c)
       {
#ifdef DEBUG// Check if DEBUG is defined
           printf("We get:%c, about to exit.\n",c);
#endif
           break;
       }
       else
       {
           printf("i = %d",i);
#ifdef DEBUG
           printf(", we get:%c",c);
#endif
           printf("\n");
       }
   }
   printf("Hello World!\n");
   return 0;
}

/*#endif is used to terminate the #if preprocessing directive.*/
  1. Usage of ifdef and #ifndef
#include <stdio.h>
#define DEBUG
main()
{
#ifdef DEBUG
   printf("yes ");
#endif
#ifndef DEBUG
   printf("no ");
#endif
}
//#ifdef defined is equivalent to #ifdef;
//#if !defined is equivalent to #ifndef
  1. #else directive
Common Uses of Preprocessor Directives in Embedded Software
  1. #elif directive
Common Uses of Preprocessor Directives in Embedded Software
  1. Other directives
  • #error directive will cause the compiler to display an error message and then stop compilation.
  • #line directive can change the file number and line number used by the compiler to indicate warning and error messages.
  • #pragma directive has no formal definition. Compilers can customize its use. A typical usage is to suppress or allow certain annoying warning messages.
END
Evaluation Center Free Application

Common Uses of Preprocessor Directives in Embedded Software

Common Uses of Preprocessor Directives in Embedded Software

👆Long press the image to scan the code to apply👆

Leave a Comment