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; }