1 预定义宏是什么
为了方便处理一些有用信息,C/C++编译器中的预处理器会定义一些预处理标识符,也就是预定义宏。这些宏定义不仅可以帮助完成源码的跨平台编译,灵活使用也可以巧妙地输出非常有用的调试信息,其具有以下特点:
- 预定义宏的名称都是以“__”(两条下划线)开头和结尾的,并且通常由大写字符组成
- 预定义宏不能被取消定义(#undef)或被编程人员重新定义
- 使用预定义宏需要包含头文件<stdio.h>
2 常用的预定义宏
下表列举了一些在C/C++编程中常用的预定义宏:
预定义宏 | 含义 |
---|---|
__DATE__ | 源文件的编译日期,以“Mmm dd yyy”形式的字符串常量表示 |
__TIME__ | 源文件的编译时间,以“hh:mm:ss”形式的字符串常量表示 |
__TIMESTAMP__ | 源文件的编译时间戳,以字符串常量表示 |
__FILE__ | 源文件的名称,以字符串常量表示 |
__LINE__ | 源文件中正被编译的行号,以十进制整型常量表示 |
__VERSION__ | 编译器的版本,以字符串常量表示 |
__STDC__ | 若当前编译器严格遵循 ANSI C 标准,则该宏值为1,否则未定义 |
__STDC_VERSION__ | 若编译器标准符合C89,该宏值为199409L; 若编译器标准符合C99,该宏值为199901L; 若编译器标准符合C11,该宏值为201112L |
__FUNCTION__ | 其展开为当前函数名,以字符串常量表示(函数作用域) |
3 预定义宏的用例
3.1 确定程序的编译时间
利用“__DATE__”和“__TIME__”预定义宏可以确定程序的编译时间,示例代码如下:
int main (void) { printf("www.nixinglong.com\n"); // N1XON's personal website printf("Compiled on %s at %s\n", __DATE__,__TIME__); return 0; }
3.2 兼容不同版本的编译器
利用“__STDC__”与“__STDC_VERSION__”预定义宏可以编写兼容不同版本编译器的程序,示例代码如下:
#ifdef __STDC__ /* Some version of standard C */ #if defined(__STDC__VERSION__)&&__STDC_VERSION__>=199901L /* C99 */ #elif defined(__STDC_VERSION__)&&__STDC_VERSION__>=199409L /* C89 and amendment 1 */ #else /* C89 but not amendment 1*/ #endif #else /* __STDC__not defined */ /*Not Standard C*/ #endif
3.3 异常跟踪
利用”__FILE__”、”__LINE__”与”__FUNCTION__”预定义宏,可以方便的在调试程序时对程序运行异常进行跟踪,示例代码如下:
#include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #include <stdlib.h> #define MESSAGE(message,assertion) \ do{\ if(!(assertion)){\ printf("line %d in %s(%s)", __LINE__, __FILE__,__FUNCTION__);\ if(message){\ printf(":%s",message);\ }\ printf("\n");\ abort();\ }\ }while(0) int OpenFile(const char *filename) { int fd; MESSAGE("文件名称不能够为空",filename); MESSAGE("文件不存在",0==access(filename,F_OK)); fd = open(filename,O_RDONLY); close(fd); return 0; } int main(int argc,char **argv) { MESSAGE("命令参数不能够为空",argc==2); OpenFile(argv[1]); return 0; }