宏在C/C++程序中的使用非常普遍,在C/C++的各种框架中,经常可以看到宏的使用。灵活的使用宏可以使代码精简强大,发挥意想不到的作用。
在C/C++编译过程中,宏处理是在预处理阶段进行的。宏的核心思想就是简单替换,下面我将对宏的一些用法进行总结,有不对的地方欢迎指正。
宏一般用预处理指令#defined定义,格式如下: #defined 标识符 替换的标识,其中个替换的标识符可以是数字,字符串,代码等等,也可以省略,如果省略了,则是我们一般常见的宏定义,这种宏不用于替换,而是有其它作用,常见的有两种:
- 头文件保护宏
- 宏开关
第一种很常见,比如:
#ifndef _HEADER_
#define _HEADER_
....
#endif
这类宏的作用就是为了防止头文件重复包含。
第二种也很常见,比如调试经常用的一类代码:
#ifdef __DEBUG_
....
#endif
这类宏就是宏开关,若定义了__DEBUG_这个宏,则编译器会编译编译里面的代码。这种编译称为条件编译。
若替换标识符为常量,比如整型、浮点型、字符串常量,这类宏就是宏常量。宏常量的定义可以在文件任何位置,类定义内部,函数内部,函数和类外部,头文件等,并且作用域从定义开始,尤其是放在头文件是,要小心宏定义重复。而且在C++中,若想定义常量最好用const而不是用宏定义。
宏常量使用是非常广泛的,在C/C++标准中就预定义了几个宏,我们称之为内置宏,比如:
- __LINE__ 当前代码行的行号
- __FILE__ 源程序的完整路径
- __DATE__ 系统日期
- __TIME__ 系统时间
- __TIMESTAMP__ 系统时间戳
- __FUNCTION__ 当前代码行所在的函数的名字
- __cplusplus 当编写C++程序时该标识符被定义
此外,在编译器中也定义了许多内置宏,比如_MSC_VER,__GNU__等等,这些宏在头文件中经常可以看到。
以上宏都是不带参数的,当然宏也可以带参数,带参数宏也叫宏函数,除了替换字符串,也要替换参数,一个比较常见的例子是:
#defined SQUARE(x) ((x)*(x))
这列宏一般要注意加括号以防止替换时出现问题。宏参数功能很强大,后面可以跟条件判断,for循环,结构体定义,代码等,灵活使用可以代码简洁强大,但是也有可能造成代码难以阅读和调试。
宏定义中还有几种操作符,比较常见的是#和##,
#的作用是将宏字符串化,即将宏添加“”。比如:
#define WARNING(STR) printf("warning " #STR "\n");
WARNING(fault happened)
#define APPEND(STR) #STR
std::string string;
string += "hello" APPEND(world)
在控制台就会打印出warning fault happened,string的内容为“hello world。
除此之外我们还可以把多个字符串连接起来,比如:
#define MULTISTRING(x,y) #x #y
另外,如果宏的参数和某个变量的标识符一样,宏是不会将其展开为变量的值,比如:
int var = 100;
printf("%s\n",STRING(var));
结果将是“var”,而不是“100”。
##的作用是把两个符号连接成一个符号,比如:
#define SYMBOL(x,y) x##y
最后再说一下可变参数宏,有了可变参数宏,宏函数可以接收不定个数参数,形参为...,在宏体内用获取形参用__VA_ARGS__,一个常见的例子是:
#ifdef __DEBUG_
#define LOG(fmt,...) fprintf(stderr,fmt,__VA_ARGS__);
#else
#define LOG(fmt,...)
#endiF
LOG("%s\n","this is bug")
以上就是我对宏的总结,如有不正确的地方欢迎提出指正和建议。