目录
#define:用于定义一个宏,「#」表示这是一条预处理指令;「define」为宏定义命令
宏:一种批量处理的称谓,程序预编译期间将所有使用的宏替换为对应「宏体」(常量、常量字符串、关键字、语句 ......)
宏定义的宏名称作「标识符」,具有常属性,不可修改
宏定义分为「无参宏定义」和「有参宏定义」
无参宏定义
#define 宏名 宏体
定义常量
#define MAX 1000 //将MAX定义为常量1000
定义关键字
#define reg register //将reg定义为register(寄存器)
定义语句
#define do_forever for(;;) //将do_forever定义为死循环
若定义语句过长需要分行,续在末尾加上续航符「\」
#define DEBUG_PRINT printf("路径:%s 行数:%d 日期:%s 时间%s",\
__FILE__,__LINE__ ,__DATE__,__TIME__)
//将DEBUG_PRINT定义为打印相应内容
有参宏定义
将对应内容替换为参数,类似函数
#define 宏名(参数) 宏体
#define SQUARE(x) x*x
int main()
{
int a = SQUARE(1);
//替换后:int a = 1 * 1
printf("%d", 1);//输出1
return 0;
}
注意事项:尽可能地将宏体、宏体内符号加上括号
#define SQUARE(x) ((x)*(x))
宏进行的是替换操作,如果不加上必要的括号,会导致结果不同于预期
不加括号:
#define SQUARE(x) x*x
int main()
{
int a = 2 + 2;
int b = 10 * SQUARE(a);
//替换后:int b = 10 * 2 + 2 * 2 + 2;
printf("%d", b);//输出26
return 0;
}
加括号:
#define SQUARE(x) ((x)*(x))
int main()
{
int a = 2 + 2;
int b = 10 * SQUARE(a);
//替换后:int b = 10 * ((2 + 2) * (2 + 2));
printf("%d", b);//输出160
return 0;
}
带副作用的参数
a + 1 //不带副作用
a++ //带副作用
#define MAX(a, b) ((a) > (b) ? (a) : (b))
int main()
{
int a = 5;
int b = 4;
int c = MAX(a++, b++);
// 替换后int c = ((a++) > (b++) ? (a++) : (b++));
printf("%d", c);// 输出:6
return 0;
}
「#」
将参数名转换为字符串
#define print(x) printf("the value of "#x" is %d",x)
int main()
{
int a = 10;
print(a);
//转换后:printf("the value of ""a"" is %d",x);
//输出:the value of a is 10
return 0;
}
「##」
将两边符号的进行合并
#define print(a,b) printf("%d",a##b)
int main()
{
int abc = 10;
print(a, bc);
//转换后:printf("%d",abc)
//输出:10
return 0;
}
「#undef」
移除指定宏定义
#define MAX 1000
#undef MAX //移除宏定义MAX
宏的优缺点
优点:
(1)无类型限制,更灵活
(2)直接替换,执行效率高
缺点:
(1)若宏体过大,替换时会大幅增加程序代码量
(2)无类型限制,不严谨
(3)无法调试
(4)不可使用带有副作用的参数
offsetof 模拟实现
#define OFFSETOF(type,name) (int)&(((type*)0)->name)
//将0转换为对应结构体类型,这时结构体的地址从0开始
//取出的成员地址正好就是相较于起始位置的偏移量
//由于最后求的是偏移量(整数),还需将地址转换为整型