1、通过宏控制打印调试信息
使用 #ifdef
预处理指令来检查是否定义了 DEBUG
标志。如果定义了 DEBUG
,则宏会打印调试信息;否则,它什么也不做。
#include <stdio.h>
#ifdef DEBUG
#define DEBUG_PRINT(fmt, ...) \
do { fprintf(stderr, fmt, __VA_ARGS__); } while (0)
#else
#define DEBUG_PRINT(fmt, ...) do { } while (0)
#endif
int main() {
int x = 10;
DEBUG_PRINT("Debug: The value of x is %d\n", x);
return 0;
}
说明:
#ifdef DEBUG
: 这检查是否定义了 DEBUG
宏。如果定义了 DEBUG
,则包含 #ifdef
块中的代码。
DEBUG_PRINT
: 该宏接受一个格式字符串和一个可变数量的参数(使用 __VA_ARGS__
)。这使您可以像使用 printf
一样传递任何格式和相应的变量。
fprintf(stderr, fmt, __VA_ARGS__)
: 这会将调试输出发送到标准错误流 (stderr
),通常用于调试信息。
do{ } while(0)
: 这是宏中常用的惯用法,用来确保宏内的代码像块一样执行(即它像一个语句)。这样可以防止宏在没有大括号的 if
语句中使用时出现问题。
2、条件编译
通过传递像 -DDEBUG
这样的编译器标志,在调试模式下定义 DEBUG
,并可以在生产模式下不定义它进行编译。
gcc -DDEBUG myprogram.c -o myprogram # 以调试模式编译 gcc myprogram.c -o myprogram # 不定义 DEBUG 编译
3、使用 stderr的原因
使用 stderr
进行调试信息打印是一个好习惯,因为它可以将正常的程序输出(stdout
)与调试或日志信息分开。这样,如果将程序的输出重定向到文件中,就不会意外地将调试信息混合在一起。
#include <stdio.h>
// 打印调试信息到 stderr
#define DEBUG_PRINT(fmt, ...) fprintf(stderr, "DEBUG: " fmt, __VA_ARGS__)
int main() {
int a = 5;
int b = 10;
// 打印调试信息
DEBUG_PRINT("a = %d, b = %d\n", a, b);
return 0;
}
4、添加时间戳
可以进一步扩展此宏,添加时间戳或函数名,使调试信息更加易于追踪,特别是在较大的项目中。
#include <stdio.h>
#ifdef DEBUG
#define DEBUG_PRINT(fmt, ...) \
do { \
fprintf(stderr, "[%s:%d] " fmt, __FILE__, __LINE__, __VA_ARGS__); \
} while (0)
#else
#define DEBUG_PRINT(fmt, ...) do { } while (0)
#endif
int main() {
int x = 10;
int y = 20;
// 调用调试宏,打印文件名和行号信息
DEBUG_PRINT("Debug: The value of x is %d\n", x);
DEBUG_PRINT("Debug: The value of y is %d\n", y);
// 其他代码逻辑
printf("This is a normal print statement.\n");
return 0;
}