Essential Embedded C Language Macro Techniques You Must Know

Essential Embedded C Language Macro Techniques You Must Know

Word Count: 2000 Practicality Index: ⭐⭐⭐⭐⭐

01
Macro Print Function

In our embedded development, using printf to print some information is a common debugging method. However, when the amount of printed information is large, it can be difficult to know which information is printed in which function.

Especially for printing exceptions, we need to quickly locate the position of the exception.

At this time, we can use macro definitions to encapsulate a macro print function, which can display information such as the file, line number, and function name where the print information is located. For example:

#define DBG_PRINTF(fmt, args...)  \
{\
 printf("<<File:%s  Line:%d  Function:%s>> ", __FILE__, __LINE__, __FUNCTION__);\
 printf(fmt, ##args);\
}

Swipe left to view all code>>>

Usage Example:

Essential Embedded C Language Macro Techniques You Must Know

Essential Embedded C Language Macro Techniques You Must Know

As you can see, the usage is the same as printf, and each print statement will print the file name, line number, and function name of the debug information at the beginning, making it easier for us to find some debugging information.

Among them, __FILE__, __LINE__, and __FUNCTION__ are built-in macro definitions of the compiler, representing the file, line number, and function where the debug information is located, respectively.

In addition, commonly used macros include: __DATE__, __TIME__, which represent the current compilation date and time, respectively. For example:

DBG_PRINTF("Compile Time: %s  %s\n", __DATE__, __TIME__);

Essential Embedded C Language Macro Techniques You Must Know

The ## symbol in the second printf is to handle the case where args does not represent any parameters. For example:

DBG_PRINTF("Hello world");

If the ## symbol is not added, the second statement of the above macro is expanded to:

printf("Hello world\n", );

As you can see, an extra comma appears, which is unnecessary.

After adding the ## symbol, the second statement of the above macro is expanded to:

printf("Hello world\n");

This is the result we want. In fact, we can clearly see these results by checking the preprocessed file:

Essential Embedded C Language Macro Techniques You Must Know

Essential Embedded C Language Macro Techniques You Must Know

Essential Embedded C Language Macro Techniques You Must Know

Finally, it is important to note that DBG_PRINTF is still different from printf. The DBG_PRINTF macro is a combination of two statements with no return value; while the prototype of printf is:

int printf (const char *__format, ...)

However, we generally rarely use the return value of printf, so the usage of DBG_PRINTF is basically consistent with that of the printf function.

02
Debug Print Macro Switch

Usually, some print debug information is only needed during our debugging phase and is not required during the program release phase.

Therefore, to avoid the resource overhead caused by printing debug information, we can comment out these print debug statements.

One method is to comment out each statement individually, which is a relatively inefficient method. A more efficient method is to add a debug macro switch and use conditional compilation to choose whether to print debug information or not.

For example, we can modify the above code as follows:

#define  DEBUG   1  

#if DEBUG
  #define DBG_PRINTF(fmt, args...)  \
  {\
    printf("<<File:%s  Line:%d  Function:%s>> ", __FILE__, __LINE__, __FUNCTION__);\
    printf(fmt, ##args);\
  }\
#else
  #define DBG_PRINTF(fmt, args...)   
#endif

Depending on the value of the DEBUG macro to choose the corresponding print macro function. When the value of DEBUG is 1, the related print debug statements are activated; when the value of DEBUG is 0, the print debug statements are disabled.

In this way, we can easily start and stop the DBG_PRINTF print debug information for our entire project by setting the value of the DEBUG macro.

03
do{}while(0)
In fact, the macro DBG_PRINTF we encapsulated above has one more flaw. For example, when we use it with if and else, there will be such a usage situation:

Essential Embedded C Language Macro Techniques You Must Know

This will report a syntax error. Why?
Similarly, we can first look at what our demo code looks like after preprocessing, and what the corresponding macro code will be transformed into. For example:

Essential Embedded C Language Macro Techniques You Must Know

Here we can see that our if and else structure code is replaced as follows:
if(c)
{ /* ....... */ };
else
{ /* ....... */ };
Obviously, a syntax error has occurred. A semicolon cannot be added after the curly braces of if; this semicolon can actually be seen as an empty statement, which separates if and else, causing else to not correctly match if, leading to a syntax error.
To solve this problem, there are several methods. The first method is to remove the semicolon. The code becomes:

Essential Embedded C Language Macro Techniques You Must Know

The second method is to always add {} when printing debug with DBG_PRINTF after if. The code becomes:

Essential Embedded C Language Macro Techniques You Must Know

Both of these methods can compile and run normally.
However, in C language, each statement often ends with a semicolon; and some people have the habit of not adding braces when there is only one statement after if; moreover, the purpose of our created DBG_PRINTF macro function is to align with the printf function, and the use of printf with semicolons anywhere is not a problem.
Based on these reasons, it is necessary to further modify our DBG_PRINTF macro function.
Next, we introduce do{}while(0) to make a simple modification to our DBG_PRINTF. The modified DBG_PRINTF macro function is as follows:
#define DBG_PRINTF(fmt, args...)  \
do\
{\
    printf("<<File:%s  Line:%d  Function:%s>> ", __FILE__, __LINE__, __FUNCTION__);\
    printf(fmt, ##args);\
}while(0)
The body of the do…while loop only executes once, and the effect is the same as not adding a loop. Moreover, it avoids the above problem. The preprocessed file:

Essential Embedded C Language Macro Techniques You Must Know

In our macro function entity, there is no semicolon after while(0), which is complemented by a semicolon during actual calls, which conforms to the habit of C language statements ending with semicolons and also conforms to the syntax rules of do…while.

Using do{}while(0) to encapsulate macro functions may seem unfamiliar to many beginners, but it must be acknowledged that this is indeed a very common method.

Search for while(0) in the STM32 HAL library:

Essential Embedded C Language Macro Techniques You Must Know

Essential Embedded C Language Macro Techniques You Must Know

Search for while(0) in the Linux source code:

Essential Embedded C Language Macro Techniques You Must Know

As you can see, do{}while(0) is widely used in practical applications.

04
# Operator and ## Operator

These two operators have been shared before, and I will mention them here as well.

<span>#</span> as a <span>preprocessing operator</span> can convert tokens into strings.

For example, if A is a macro parameter, then #A converts it to the string “A” of the parameter name. This process is called <span>stringizing</span>. The following program demonstrates this process:

Essential Embedded C Language Macro Techniques You Must Know

Essential Embedded C Language Macro Techniques You Must Know

The ## operator can combine two tokens into one token. The following program demonstrates this process:

Essential Embedded C Language Macro Techniques You Must Know

Essential Embedded C Language Macro Techniques You Must Know

This operator is used a lot. For example:

Essential Embedded C Language Macro Techniques You Must Know

Essential Embedded C Language Macro Techniques You Must Know

1

“13 Tips! Essential Optimization Techniques in Embedded C You Must Know…”

2

“8000 Words! Must-Know Data Storage and Programming Knowledge in Embedded C!”

3

“20 Common Interview Questions for Embedded Engineers (with Answers)”

Essential Embedded C Language Macro Techniques You Must Know

Essential Embedded C Language Macro Techniques You Must Know

This Week’s Live Broadcast | Click to View👇

Essential Embedded C Language Macro Techniques You Must Know

Monday | IoT Project Development Based on STM32

1. How to implement information collection?

2. How to conduct Zigbee network development?

Essential Embedded C Language Macro Techniques You Must Know

Tuesday | Master SPI Communication Principles and Applications in 1 Hour

1. SPI bus communication principles

2. Differences between SPI and IIC

3. Application scenarios of communication buses

Essential Embedded C Language Macro Techniques You Must Know

Find It Useful? Click to See

Essential Embedded C Language Macro Techniques You Must Know

Leave a Comment