Word Count: 2000 Practicality Index: ⭐⭐⭐⭐⭐
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>>>
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__);
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:
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.
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.
if(c)
{ /* ....... */ };
else
{ /* ....... */ };
#define DBG_PRINTF(fmt, args...) \
do\
{\
printf("<<File:%s Line:%d Function:%s>> ", __FILE__, __LINE__, __FUNCTION__);\
printf(fmt, ##args);\
}while(0)
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:
Search for while(0) in the Linux source code:
As you can see, do{}while(0) is widely used in practical applications.
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:
The ## operator can combine two tokens into one token. The following program demonstrates this process:
This operator is used a lot. For example:
“13 Tips! Essential Optimization Techniques in Embedded C You Must Know…”
“8000 Words! Must-Know Data Storage and Programming Knowledge in Embedded C!”
“20 Common Interview Questions for Embedded Engineers (with Answers)”

This Week’s Live Broadcast | Click to View👇

Monday | IoT Project Development Based on STM32
1. How to implement information collection?
2. How to conduct Zigbee network development?

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
Find It Useful? Click to See
