在写一个代码生成可执行文件的过程需要经过编译和链接,编译又要经过三部:预处理,编译,汇编。
#define定义的变量和宏就是在预处理阶段会处理的。
一个简单的宏定义:
#include<stdio.h>; #define Max(a,b) a>b?a:b int main() {int x = 4, k = 9;printf("%d", Max(x, k)); }
Max:宏名
a,b:宏参数
a>b?a:b:宏体
宏定义有些类似函数,Max(a,b)会被替换为 a>b?a:b
比如这里printf("%d",Max(x,k));在预处理阶段会被替换为printf("%d",x>k?x:k);
注意:在#define定义的宏或变量后面不能加分号(;)
例如:
1.运算符优先级处理
思考下面输出结果:
#define Mul(a,b) a*b int main() {int x = 4, k = 3;printf("%d", Mul(x, k + 2));return 0; }
这里printf("%d", Mul(x, k + 2));会被替换为printf("%d",x*k+2);
所以结果为14,注意这里只是一一替换在预处理阶段进行,结果并不是20,而计算阶段是再生成的.exe后缀文件执行以后的事,所有在定义宏的时候,为了达到想要的效果我们通常在宏体里面加一些括号来避免这种问题的出现
例如:
#define Mul(a,b) ((a)*(b))
2.带副作用的宏参数
在给宏传参的时候我们尽量避免传一些前缀或后缀++ --这样的参数,这是一些带有副作用的参数
例如:
#define Max(a,b) ((a)>(b)?(a):(b))//添加括号是解决优先级带来的潜在问题,上一个板块讲到 int main() {int x = 2, k = 3;printf("%d", Max(++x, ++k)); }
程序执行后输出5,这里x变为3,k变为5。结果是我们无法预料的。
3.函数与宏的区别:
4.宏替换的规则
在程序中扩展#define定义符号和宏时,需要涉及⼏个步骤。
1. 在调⽤宏时,⾸先对参数进⾏检查,看看是否包含任何由#define定义的符号。如果是,它们⾸先被替换。
2. 替换⽂本随后被插⼊到程序中原来⽂本的位置。对于宏,参数名被他们的值所替换。
3. 最后,再次对结果⽂件进⾏扫描,看看它是否包含任何由#define定义的符号。如果是,就重复上述处理过程。
注意:
1. 宏参数和#define定义中可以出现其他#define定义的符号。但是对于宏,不能出现递归。
2. 当预处理器搜索#define定义的符号的时候,字符串常量的内容并不被搜索
5.#运算符的使用
#运算符将宏的⼀个参数转换为字符串字⾯量。它仅允许出现在带参数的宏的替换列表中。
#运算符所执⾏的操作可以理解为”字符串化“。
当我们有⼀个变量 int a = 10; 的时候,我们想打印出: the value of a is 10 .
就可以写:
#define Print(n) printf("the value of "#n" is %d", n); int main() {int a = 0;Print(a);return 0; }
而写作下面的形式只会打印 the value of n is 10,其中的n并不会被替换
#define Print(n) printf("the value of n is %d", n);
6.##运算符的使用
## 可以把位于它两边的符号合成⼀个符号,它允许宏定义从分离的⽂本⽚段创建标识符。 ## 被称
为记号粘合
这样的连接必须产⽣⼀个合法的标识符。否则其结果就是未定义的。
这⾥我们想想,写⼀个函数求2个数的较⼤值的时候,不同的数据类型就得写不同的函数
比如:int int_max(int x, int y) {return x>y?x:y; }float float_max(float x, float y) {return x>yx:y; }
这么写很繁琐我们可以用宏定义来写函数
7.宏定义函数
#define Max(type) type type##_max(type x,type y)\{ \return x>y?x:y; \} 这里\为续行符 Max(int) Max(float) Max(char) int main() {printf("%d\n", int_max(5, 9));printf("%.2f\n", float_max(5.2, 5.32));printf("%c\n", char_max('p', 'h')); }