导语
本篇是关于宏定义的一些知识总结
1. 预处理 preprocess
根据程序中的预处理指令,预处理器把宏名替换成其表示的内容–宏展开(macro expansion);
预处理器指令从#开始运行,到后面的第1个换行符为止(指用Enter按下的换行,而不是 \n)。即:指令的长度仅限于一行;
在预处理开始前,编译器会把多行物理行(physical line)处理为一行逻辑行(logical line)。即:找到反斜杠\后面的换行符,把反斜杠和换行符删掉
双引号字符串中的文本不会被宏替换!
2. 宏定义常量和表达式
宏定义只做简单的替换,不计算,不对表达式求值1
2
3
4
5
*4) // 反斜杠扩展逻辑行
关于 #define 和 const 定义常量
#define 定义的常量是直接替换,没有类型检查,而 const 有对应的数据类型,有类型检查
#define 仅展开,不分配内存;const 会分配内存;#define 定义的常量占用代码段的内存,const 定义的常量占用数据段的内存
#define 可以通过 #undef 取消再重新定义,const 不能重定义。
建议!!!只使用const常量而不使用宏常量。
3. 在宏定义中使用参数
1 |
建议!!!用()把所有的参数和整个替换体包裹起来
建议!!!不要在宏中使用递增或递减运算符(++,–)
3.1 运算符 #,##,以及宏VA_ARGS
3.1.1 # :把记号(token)转换成字符串
例如,如果x是一个宏形参,那么#x就是转换为字符串”x”的形参名。这个过程称为字符串化(stringizing)1
2
3.1.2 ## :连接运算符
1 |
3.1.3 __VA_ARGS__宏
__VA_ARGS__就是把…表示的参数包,原封不动的抄过去
… 表示可变的参数(variadic argument),包括里面的逗号
1 | //可变参数不可为空 |
如果可变参数为空,就会出现一个多余的逗号printf(,)会报错
“##”操作符如果一侧没有内容就会连接失败,并且删除两侧要连接的内容,此处就会删除这个多余的逗号
也可以给可变参数…起个名字,而不用__VA_ARGS__,使用方法和效果是完全一样的
1 |
|
测试代码及结果
1 |
|
其他
预处理指令:#define、#include、#ifdef、#else、#endif、#ifndef、#if、#elif、#line、#error、#pragma
关键字:_Generic、_Noreturn、_Static_assert
函数/宏:sqrt()、atan()、atan2()、exit()、atexit()、assert()、memcpy()、memmove()、va_start()、va_arg()、va_copy()、va_end()