借调试宏的设计,梳理下C语言宏的用法函数
嵌入式设备基本会配置RS232串口做为调试IO接口,假设底层串口单字节输出函数为SERIAL_PutChar(),利用fputc()和fputs()重定向printf函数设计
void fputc(int byte, FILE* stream) { (void)stream; SERIAL_PutChar(byte); } void fputs(const char *pstr, FILE *stream) { (void)stream; while(*pstr) { SERIAL_PutChar(*pstr++); } }
这样在代码里面利用printf()函数输出的字符串都老老实实从调试串口出来调试
某个C驱动模块,但愿在调试时打印调试信息,而产品代码中不显示调试信息。code
#define DRV_DEBUG 1 #if DRV_DEBUG #define DRV_PRINT(x) printf(x) #else #define DRV_PRINT(x) #endif
这个版本的DRV_PRINT(x)只能输出单变量——纯字符串接口
void foo() { DRV_PRINT("Driver Initialize Success!"); }
不须要打印调试信息时,更改DRV_DEBUG宏定义ip
#define DRV_DEBUG 0
固然也能够直接这样定义字符串
#define DRV_PRINT printf
可是若是宏调用了多个参数:编译器
void foo() { DRV_PRINT("Driver Initialize Success: ver %d.%d !", 1, 2); }
产品代码中的#define DRV_PRINT(x)
将编译错误!产品
怎么办?一种处女座确定接受不了的作法,多加对括号
it
void foo() { DRV_PRINT(("Driver Initialize Success: ver %d.%d !", 1, 2)); }
无论是调试代码仍是产品代码,编译都OK
#define DRV_DEBUG 1 #if DRV_DEBUG #define DRV_PRINT(fmt, val1, val2) printf(fmt, val1, val2) #else #define DRV_PRINT(fmt, val1, val2) #endif
若是只须要打印一个变量,第2
个参数用随意值填位,如
void foo() { DRV_PRINT("Driver Initialize Success: ver %d !", val1, 2); }
相似,若是有4个参数,就:
void foo() { DRV_PRINT("Driver Initialize Success: ver %d !", val1, 2, 3, 4); }
很傻,可是没办法:(,VxWorks 5.5
内核代码里就是这样干的!
C90和C++中可将宏声明为接受可变数量的自变量,如ARM编译器
是这样的:
#define DRV_DEBUG 1 #if DRV_DEBUG #define DRV_PRINT(fmt, ...) printf(fmt, __VA_ARGS__) #else #define DRV_PRINT(fmt, ...) #endif
如今DRV_PRINT
用法和printf
彻底同样了,这么爽的功能,C2000编译器
却不支持!
题外话,注意这个特性C90支持,而C90是C++的一个子集,可是C99和C++却不兼容了
有时候,某个模块,有输入跟踪信息,输出信息,错误信息等,若是我想单独打开某部分信息,能够这样设计
#define DRV_DEBUG 1 #define DRV_DEBUG_IN 0x0001 #define DRV_DEBUG_OUT 0x0002 #define DRV_DEBUG_ERR 0x0004 #define DRV_DEBUG_ALL 0xFFFF #if DRV_DEBUG unsigned int drv_flags = DRV_DEBUG_ERR | DRV_DEBUG_OUT; #define DRV_PRINT(flag, fmt, ...) \ do{\ if(drv_flags & flag){\ printf(fmt, __VA_ARGS__)}\ }while(0) #else #define DRV_PRINT(fmt, ...) #endif
NOTE: 多行宏,注意换行前加
\
号
这样,我只打印OUT和ERR:
void drv_write(char* msg_out) { DRV_PRINT(DRV_DEBUG_OUT, "Drivr write %s", msg_out); // 输出 DRV_PRINT(DRV_DEBUG_ERR, "Drivr write %s", msg_out); // 输出 DRV_PRINT(DRV_DEBUG_IN, "Drivr write %s", msg_out); // 不输出 }
进一步,能够设计针对整个系统不一样模块的LOG输出控制!TCP/IP协议栈实现Lwip
就是这么干的!
在嵌入式C语言里面,运用printf调试宏,有助于过后分析,定位BUG,多多益善!