2. 宏定义
宏(Macro)是在预处理阶段,将一个字符串和一个标识符相关联,用于在预处理阶段进行字符串替换。
宏大致可以分为以下几种:
- 不带参数的宏
- 带有参数的宏
- 编译器内预定义的宏
不带参数的宏定义的语法
#define 宏名 宏的内容
带有参数的宏定义的语法
#define 宏名(参数名1, 参数名2, ...) 宏的内容
说明:
- 宏名必须是一个标识符,一般为了和变量名做区分,一般宏名都大写。
#define和宏名以及 宏的内容 之间至少有一个空格或制表符分隔。- 宏的内容部分必须写在一行内,如果过长需要换行书写,则要在需要换行的行尾最后一个字符添加折行符(
\)。 - 宏的内容部分如果不需要可以为空。
- 带有参数的宏的参数部分需要给出参数名,但不需要给出参数类型。
- 在带有参数的宏的内容中,如果有参数名出现,则用实际传入参数代替内容部分的参数名。
- 如果两次或以上使用
#define指令定义同一个宏名则宏的定义方式必须完全相同,否则报错。
示例
- 使用不带参数的宏
PI代替圆周率的浮点数字面值3.1415926。 - 使用带有参数的宏
AREA(rr)替代求圆的面积的表达式3.14*rr*rr。其中 rr 是参数,在替换处会使用参数字符串替换。
// filename: circle.c
#define PI 3.1415926
#define AREA(rr) 3.14*rr*rr
int main(int argc, char * argv[]) {
double r = 10; //圆的半径
double length = PI * r * 2; // 计算圆的周长。
double area1 = PI * r * r; // 计算面积
double area2 = AREA(r); // 计算面积
printf("圆的半径: %f\n", r);
printf("圆的周长: %f\n", length);
printf("圆的面积1: %f\n", area1);
printf("圆的面积2: %f\n", area1);
return 0;
}
使用 gcc -E circle.c 命令预处理后的结果如下:
weimingze@mzstudio:~$ gcc -E circle.c
# 0 "temp.c"
# 0 "<built-in>"
# 0 "<command-line>"
# 1 "/usr/include/stdc-predef.h" 1 3 4
# 0 "<command-line>" 2
# 1 "temp.c"
int main(int argc, char * argv[]) {
double r = 10;
double length = 3.1415926 * r * 2;
double area1 = 3.1415926 * r * r;
double area2 = 3.14*r*r;
printf("圆的半径: %f\n", r);
printf("圆的周长: %f\n", length);
printf("圆的面积1: %f\n", area1);
printf("圆的面积2: %f\n", area1);
return 0;
}
可见在实际使用时 PI 会替换成 3.1415926; AREA(r) 被替换成了 3.14*r*r。
需要注意的问题:
宏的替换只是源码级别的替换,不会做类型的判断。如我们在使用宏时写成AREA(r+1) 则会被替换成为 3.14*r+1*r+1,显然这不是我们想要的结果,因为乘法的运算符的优先级大于加法的优先级。
为了解决上述问题,我们可以尝试将宏定义 AREA(r) 的宏的内容部分将参数加上括号,如: #define AREA(rr) 3.14*(rr)*(rr),这样AREA(r+1) 将会替换成 3.14*(r+1)*(r+1),上述问的得以解决。
上述带有参数的宏中,如果再使用宏 AREA 是写成 AREA(++i),则会被替换成为 3.14*(++i)*(++i),显然使用者的原意是执行一次 ++i, 但替换后就会执行两次 ++i且结果也不正确,因此在使用宏的时候尽可能避免上述问题。
宏的优缺点
优点
- 使用带有参数的宏定义可以象函数一样使用,但没有函数调用的开销,执行效率高。
- 与类型无关,可以编写出于类型无关的通用代码。
缺点
- 预处理阶段展开,难以调试。
- 容易出错,带有参数的宏的参数可能会被多次求值。
- 没有类型检查,在编辑阶段报错而非预处理阶段报错。
练习
编写带有参数的宏 MY_MAX(a,b) 返回参数 a 或 b 的最大值。
如:
printf("%d\n", MY_MAX(100, 200)); // 打印 200
printf("%d\n", MY_MAX(99, 88)); // 打印 99