预处理详解(一)
目录
- 预定义符号
- define定义常量
- define定义宏
- 宏替换的规则
- 宏与函数的对比
预定义符号
C语⾔设置了⼀些预定义符号,可以直接使用,预定义符号也是在预处理期间处理的。
__FILE__ //进⾏编译的源⽂件
__LINE__ //⽂件当前的⾏号
__DATE__ //⽂件被编译的⽇期
__TIME__ //⽂件被编译的时间
__STDC__ //如果编译器遵循ANSI C,其值为1,否则未定义
举个例子:
printf("file:%s line:%d\n", __FILE__, __LINE__);
define定义常量
基本语法:
#define name stuff
举例:
#define MAX 1000
#define reg register //为 register这个关键字,创建⼀个简短的名字
#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 M 100;
建议是不要加,因为在一些情况下可能会出现报错。
#define M 100;
int main()
{int a = 0;scanf("%d", &a);if (a)a = M;elsea = 0;return 0;
}
因为预编译时他会把M替换成“100;”,这样在预编译时if语句就会变成:
if(a)
a=100;;
这样if在没有{}的情况下后面跟了一个语句和一个空语句就会报错。
define定义宏
#define机制包括了⼀个规定,允许把参数替换到文本中,这种实现通常称为宏(macro)或定义宏(define macro)。
定义宏的方法:
#define name( parament-list ) stuff
其中parament-list是一个参数,可以替换到文本stuff中。
例如:
#define ADD(X) (x)+(x)
用define定义一个用来加法的宏。
但是我们在定义宏时不要吝啬“()”,否则也会出现一些问题,比如:
#define MUL(x) x*x
int main()
{
int ret = MUL(5+1) ;
//与编译后他会变成:
//int ret = 5+1*5+1;
return 0;
}
所以我们在定义宏时要考虑一些因素,比如相邻操作符的优先级与相互影响。
同时我们在使用宏定义时也要避免一些特别参数的传参,比如“x++”:
#define MAX(a, b) ( (a) > (b) ? (a) : (b) )
...
x = 5;
y = 8;
z = MAX(x++, y++);
printf("x=%d y=%d z=%d\n", x, y, z);//输出的结果是什么?
参数写为x++时,会在宏的使用过程中,改变x的值,导致我们宏的预期结果与想象的略有偏差。
宏替换的规则
在程序中扩展#define定义符号和宏时,需要涉及几个步骤。
- 在调用宏时,首先对参数进行检查,看看是否包含任何由#define定义的符号。如果是,它们首先被替换。
也就是如下类型先替换:
#define M 100
- 替换文本随后被插入到程序中原来文本的位置。对于宏,参数名被他们的值所替换。
- 最后,再次对结果文件进行扫描,看看它是否包含任何由#define定义的符号。如果是,就重复上述处理过程。
- 宏参数和#define定义中可以出现其他#define定义的符号。但是对于宏,不能出现递归。
- 当预处理器搜索#define定义的符号的时候,字符串常量的内容并不被搜索。
例如:
#define M 100
printf("the value is M");
宏与函数的对比
宏通常被应用于执行简单的运算。
比如在两个数中找出较大的⼀个时,写成下面的宏,更有优势⼀些。
#define MAX(a, b) ((a)>(b)?(a):(b))
有人会说函数编写起来也很简单,为什么说宏会更好?
原因有⼆:
- 用于调用函数和从函数返回的代码可能比实际执行这个小型计算工作所需要的时间更多。所以宏比函数在程序的规模和速度方面更胜⼀筹。
- 更为重要的是函数的参数必须声明为特定的类型。所以函数只能在类型合适的表达式上使用。反之这个宏怎可以适用于整形、长整型、浮点型等可以用> 来比较的类型。宏的参数和类型无关。
和函数相比宏的劣势:
每次使用宏的时候,⼀份宏定义的代码将插入到程序中。除非宏比较短,否则可能大幅度增加程序的长度。
宏是没法调试的。
宏由于类型无关,也就不够严谨。
宏可能会带来运算符优先级的问题,导致程容易出现错。
宏有时候可以做函数做不到的事情。比如:宏的参数可以出现类型,但是函数做不到。
#define MALLOC(num, type)\(type*)malloc(num * sizeof(type))...
//使⽤MALLOC(10, int);//类型作为参数
//预处理器替换之后:(int*)malloc(10 * sizeof(int));
宏与函数的对比(这个是参考的素材,所以有水印请谅解):