一:#define
1:#define定义标识符
1.1:语法形式
#define name stuff
1.2:实例
#define MAX 1000 #define reg register //为 register(寄存器)这个关键字,创建一个简短的名字 // 如果定义的 stuff过长,可以分成几行写,除了最后一行外,每行的后面都加一个反斜杠(续行符)。 #define DEBUG_PRINT printf("file:%s\tline:%d\t \ date:%s\ttime:%s\n" ,\ __FILE__,__LINE__ , \ __DATE__,__TIME__ )
在#define定义标识符之后后面不能加; 例如: #define M 100; int main() { int a = 0; int b = 0; if (a < 5) b = M; else b = -1; return 0; }
此时会报错
当替换后: int main() { int a = 0; int b = 0; if (a < 5) b = 100;; else b = -1; return 0; } //也就是说else语句前不是if语句,而是空语句即; 所以报错中有:没有匹配if的非法else
那么,怎么书写宏才能够避免这种情况呢?
#define M(x) do{x=100;}while(0) int main() { int a = 0; int b = 0; if (a < 5) M(b); else b = -1; return 0; }
此时,代码成功执行
宏替换后:
#define M(x) do{x=100;}while(0) //这里while(0)后面不要加; int main() { int a = 0; int b = 0; if (a < 5) do { b=100; }while(0); else b = -1; return 0; }
我们可以再看一个例子
//如果不加换行符会报错 #define INIT_VAL(a,b) do{\ a = 0;\ b = 0; \ }while(0) //这里while(0)后面不要加; int main() { int x = 10; int y = 20; printf("before: x = %d, y = %d\n", x, y); if (1) { INIT_VAL(x, y); } else { x = 100; y = 100; } printf("after: x = %d, y = %d\n", x, y); return 0; }
不加换行符会报错
宏替换后:
#define INIT_VAL(a,b) do{\ a = 0;\ b = 0; \ }while(0) //这里while(0)后面不要加; int main() { int x = 10; int y = 20; printf("before: x = %d, y = %d\n", x, y); if (1) { do { x=0; y=0; }while(0); } else { x = 100; y = 100; } printf("after: x = %d, y = %d\n", x, y); return 0; }
所以,如果我们想用一个宏向目标代码处插入多条数据时,可以将这多条语句封装到do…while(0)语句中,此时这个语句中可以任意添加语句去成批替换
此处do…while(0)循环的作用:
(1)do…while(0)循环具有花括号语句:可以让宏在替换的时候能够替换多条语句
(2):while(0):让这个循环只执行一次,实现替换
这种结构被称为:do-while-zero结构
2:#define定义宏
2.1:宏定义的介绍
#define机制包括了一个规定,允许把参数替换到文本中,这种实现通常称为宏(macro)或定义 宏(define macro)。 下面是宏的声明方式: #define name( parament-list ) stuff 其中的 parament - list 是一个由逗号隔开的符号表,它们可能出现在stuff中。 注意: 参数列表的左括号必须与name紧邻。 如果两者之间有任何空白存在,参数列表就会被解释为stuff的一部分
2.2:宏定义的替换规则引例
//例如: #define MAX(x,y) ((x)>(y)?(x):(y)) //反例: #define SQUARE(x) x*x int main() { int a = 3; int r = SQUARE(a); printf("r=%d\n", r); r = SQUARE(a+2); printf("r=%d\n", r); return 0; }
我们推测答案应该是9跟25,但是:
这说明宏定义跟函数有所不同,函数是先计算a+2得出5后再传递给函数的形参,但是宏却不一样
那么这么结果到底是怎么得出的呢?
#define SQUARE(x) x*x int main() { int a = 3; int r = 3*3; printf("r=%d\n", r);//3*3=9 r = 3+2*3+2; printf("r=%d\n", r);//3+2*3+2=11 return 0; }
这样我们就能解释11是怎么得出的了
事实上,宏定义就是这种替换方式,宏定义在编译阶段就已经替换进了代码中
也就是说编译后的代码中是没有类似于这种代码的int r = SQUARE(a);
而是替换成了int r = 3*3;
而:#define SQUARE(x) x*x就直接被删除掉了
那么我们到底应该怎么改呢?
2.3:宏定义的替换规则
所以用于对数值表达式进行求值的宏定义都应该加上括号
避免在使用宏时由于参数中的操作符或邻近操作符之间不可预料的相互作用。
改为:
#define SQUARE(x) ((x)*(x)) int main() { int a = 3; int r = SQUARE(a); printf("r=%d\n", r); r = SQUARE(a + 2); printf("r=%d\n", r); return 0; }
可见,此时准确得出了我们想要的结果
那么此时替换为了什么呢?
#define SQUARE(x) ((x)*(x)) int main() { int a = 3; int r = ((3)*(3)); printf("r=%d\n", r);//9 r = ((3+2)*(3+2)); printf("r=%d\n", r);//25 return 0; }
可见,加括号后的确可以防止操作符优先级的干扰,那么到底要加多少括号才能万无一失呢?
建议是能加的地方都加上括号.
接下来我们再来看一个例子:
#define DOUBLE(x) (x) + (x) 定义中我们使用了括号,想避免之前的问题,但是这个宏可能会出现新的错误。 int a = 5; printf("%d\n", 10 * DOUBLE(a)); 这将打印什么值呢? 看上去,好像打印100,但事实上打印的是55. 我们发现替换之后: printf("%d\n", 10 * (5) + (5)); 由于:乘法运算先于宏定义的加法,所以出现了55 这个问题的解决办法是在宏定义表达式两边加上一对括号就可以了。
即这样
#define DOUBLE(x) ((x) + (x)) 我们发现替换之后: printf("%d\n", 10 * ((5) + (5))); 即50
3:#define替换规则
#define 替换规则 在程序中扩展#define定义符号和宏时,需要涉及几个步骤。 1. 在调用宏时,首先对参数进行检查,看看是否包含任何由#define定义的符号。如果是,它们首先 被替换。 2. 替换文本随后被插入到程序中原来文本的位置。对于宏,参数名被他们的值所替换。 3. 最后,再次对结果文件进行扫描,看看它是否包含任何由#define定义的符号。如果是,就重复上 述处理过程。 注意: 1. 宏参数和#define 定义中可以出现其他#define定义的符号。但是对于宏,不能出现递归。 2. 当预处理器搜索#define定义的符号的时候,字符串常量的内容并不被搜索。