语法
#define 标识符 [替换列表]
#define 标识符(形参) 替换列表
#define 标识符(形参,…) 替换列表 (C99 起)
#define 标识符(…) 替换列表 (C99 起)
#undef 标识符
解释
#define 指令
#define 指令定义 标识符 为宏,即它们指示编译器将所有 标识符 的后继出现替换为 替换列表 ,可以可选地附加地处理。若已定义该标识符为任何类型的宏,则程序为病式,除非定义相同。
仿对象宏
仿对象宏将所定义的 标识符 的每次出现替换为 替换列表 。
示例
#define PI 3.14
int main() {
int a = PI;
return 0;
}
这段代码在预编译后将会被替换成
int main() {
int a = 3.14;
return 0;
}
仿函数宏
类函数宏将所定义的 标识符 的每次出现替换为 替换列表 ,额外地接受数个参数,它们会替换 替换列表 中任何 形参 的对应出现。
与函数用法类似,但请注意这并不是函数
示例
#define MUL(a, b) a * b
int main() {
int a = MUL(2, 5);
//为了让大家理解替换文本宏只是替换所以写了这一句代码
MUL(int, p) = &a;
return 0;
}
这段代码在预编译后将会被替换成
int main() {
int a = 2 * 5;
int * p = &a;
return 0;
}
# 与 ## 运算符
在仿函数宏中, 替换列表 中标识符前的 # 运算符对标识符做形参替换,并将结果环绕在引号中,等效地创建一个字符串字面量。而且,预处理器会添加反斜杠,以转义环绕内嵌字符串字面量的引号,若存在,并按需在字符串中双写反斜杠。它移除所有前导和尾随空白符,且将任何文本中部的空白符序列(但非内嵌字符串字面量中的)缩减成单个空格。此运算被称作“字符串化”。若字符串化的结果不是合法字符串字面量,则行为未定义。
示例
#出现在 VA_ARGS 前时,将整个展开的 VA_ARGS 放入引号:
#define showlist(...) puts(#__VA_ARGS__)
showlist(); // 展开成 puts("")
showlist(1, "x", int); // 展开成 puts("1, \"x\", int")
替换列表 中任意二个相继标识符间的 ## 运算符对二个标识符进行参数替换,然后连接结果。此运算被称作“连接”或“记号粘贴”。只可以粘贴有一同组成合法记号的记号:组成较长标识符的标识符、组成数字的数位或组成 += 的运算符 + 与 = 。不能通过粘贴 / 与 * 创建注释,因为注释被认为在宏替换前移除。若连接的结果不是合法记号,则行为未定义。
注意:一些编译器提供允许 ## 出现在逗号后和 VA_ARGS 前的扩展,在此情况下 ## 在 VA_ARGS 非空时无效,但在 VA_ARGS 为空时移除逗号:这使得可以定义如 fprintf (stderr, format, ##VA_ARGS) 的宏。
示例
#define print(fmt, args...) printf(fmt, ##args)
#undef 指令
#undef 指令解除定义 标识符 ,即它取消先前 #define 对 标识符 的定义。若标识符无与之关联的宏,则忽略此指令。
常用的预定义宏
宏命令 | 作用 |
---|---|
__FILE__ | 展开成当前文件名,为字符串字面量,可用 #line 指令更改 |
__LINE__ | 展开成源文件行号,为整数常量,可用 #line 指令更改 |
__DATE__ | 展开成翻译的日期,格式为 “Mmm dd yyyy” 的字符串字面量。月份名如同以 asctime 生成,而若月之日期小于 10 则 “dd” 的首字符为空格 |
__TIME__ | 展开成翻译的时间,格式为 “hh:mm:ss” 的字符串字面量,如同asctime()所生成 |
当然预定义的宏远远不止这些,还有很多我也没有列举出来,当大家有需要的时候可以查阅手册。上面的代码大家也可以自行运行测试,预编译指令为:
gcc -E filename.c >> output.c
g++用法相同
写在最后
这次的分享就到这里,如果有什么错误欢迎各位大佬指正,感谢各位看到这里!