目录
编译本身也分为几个阶段:
预编译->编译->汇编->链接
预处理指令是在预编译阶段执行的,#define就是在预处理阶段执行的语句
下面就来介绍一下#define的使用
-
#define定义标识符
语法:name为标识符,stuff为用于替换标识符的语句
#define name stuff
举例:
#define MAX 1000
#define reg register //为 register这个关键字,创建一个简短的名字
#define do_forever for(;;) //用更形象的符号来替换一种实现
#define CASE break;case //在写case语句的时候自动把 break写上。
// 如果定义的 stuff过长,可以分成几行写,除了最后一行外,每行的后面都加一个反斜杠(续行符)。
#define DEBUG_PRINT printf("file:%s\tline:%d\t \
date:%s\ttime:%s\n" ,\
__FILE__,__LINE__ , \
__DATE__,__TIME__ )
注意,在define定义标识符的时候,最好不要在最后加上 ;
如:
#define MAX 1000;
#define MAX 1000
这样容易导致问题出现
例如:
if(condition)
max = MAX;
else
max = 0;
若定义标识符的时候为#define MAX 1000; 则在预处理阶段MAX就会被替换成1000; 即max=1000;; 这就出现了语法问题,所以在定义的时候不要在最后加上分号
-
#define定义宏
#define 机制包括了一个规定,允许把参数替换到文本中,这种实现通常称为宏(macro)或定义宏(define macro)。
宏的声明方式:
#define name( parament-list ) stuff
其中的 parament-list 是一个由逗号隔开的符号表,可以理解参数,它们可能出现在stuff中。
举例:
#define SQUARE( a ) a * a
那么在声明后,程序中写入SQUARE(5)的时候,该表达式就会被替换成 5 * 5
注意:在宏替换的时候,是直接替换,不会为替换内容加上括号,例如代码:
int a = 5;
printf("%d\n" ,SQUARE( a + 1) );
在宏替换后,语句实际为printf ("%d\n",a + 1 * a + 1 )
所以程序运行结果为 5 + 1*5 +1 = 5+5+1 = 11
但是我们逻辑上希望的运行结果是求(a+1)的二次方,要实现这样的逻辑,就要在宏定义的时候加上括号:
#define SQUARE( a ) (a) * (a)
那预处理之后程序就会为预期效果了:
printf ("%d\n",(a + 1) * (a + 1) );
-
#define的替换规则
在程序中扩展#define定义符号和宏时,需要涉及几个步骤
- 在调用宏时,首先对参数进行检查,看看是否包含任何由#define定义的符号。如果是,它们首先被替换。
- 替换文本随后被插入到程序中原来文本的位置。对于宏,参数名被他们的值所替换。
- 最后,再次对结果文件进行扫描,看看它是否包含任何由#define定义的符号。如果是,就重复上述处理过程
注意:
- 宏参数和#define 定义中可以出现其他#define定义的符号。但是对于宏,不能出现递归。
- 当预处理器搜索#define定义的符号的时候,字符串常量的内容并不被搜索。
-
宏与函数的对比
宏通常被应用于执行简单的运算,比如在两个数中找出较大的一个,如:
#define MAX(a, b) ((a)>(b)?(a):(b))
为什么不用函数:
- 用于调用函数和从函数返回的代码可能比实际执行这个小型计算工作所需要的时间更多。所以宏比函数在程序的规模和速度方面更胜一筹。
- 更为重要的是函数的参数必须声明为特定的类型。所以函数只能在类型合适的表达式上使用。反之这个宏怎可以适用于整形、长整型、浮点型等可以用 > 来比较的类型。宏是类型无关的。
宏的缺点:
- 每次使用宏的时候,一份宏定义的代码将插入到程序中,除非宏比较短,否则可能大幅度增加程序的长度
- 宏是在预编译的时候直接替换,没法调试
- 宏由于类型无关,也就不够严谨
- 宏可能会带来运算符优先级的问题,导致程容易出现错
宏与函数的对比
属性 | #define定义宏 | 函数 |
代码长度 | 每次使用时,宏代码都会被插入到程序中。除了非常小的宏之外,程序的长度会大幅度增长 | 函数代码只出现于一个地方;每次使用这个函数时,都调用那个地方的同一份代码 |
执行速度 | 更快 | 存在函数的调用和返回的额外开销,所以相对慢一些 |
操作符优先级 | 宏参数的求值是在所有周围表达式的上下文环境里,除非加上括号,否则邻近操作符的优先级可能会产生不可预料的后果,所以建议宏在书写的时候多些括号。 | 函数参数只在函数调用的时候求值一次,它的结果值传递给函数。表达式的求值结果更容易预测。 |
带有副作用的参数 | 参数可能被替换到宏体中的多个位置,所以带有副作用的参数求值可能会产生不可预料的结果。 | 函数参数只在传参的时候求一次,结果更容易控制 |
参数类型 | 宏的参数与类型无关,只要对参数的操作是合的,它就可以使用于任何参数类型。 | 函数的参数是与类型有关的如果参数的类型不同,就需要不同的函数,即使他们执行的任务是不同的。 |
调试 | 宏是不方便调试的 | 函数是可以逐语句调试的 |
递归 | 宏是不能递归的 | 函数是可以递归的 |