1.预定义符号
c语言中有一些预定义符号,也是在预处理阶段处理的,可以直接使用:
__FILE__ // 进行编译的源文件
__LINE__ // 文件当前的行号
__DATE__ // 文件被编译的⽇期
__TIME__ // 文件被编译的时间
__STDC__ // 如果编译器遵循ANSI C,其值为1,否则未定义
例如:
printf("file:%s line:%d\n", __FILE__, __LINE__);
2.2.#define 定义常量
#define可以在mian函数外定义常量,用自定义的名字来表示
基本形式:
# 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__)
3.#define定义宏
#define 机制包括了⼀个规定,允许把参数替换到文本中,这种实现通常称为宏或定义宏。
宏的基本形式:
# define name( parament-list ) stuff
其中parament-list 是⼀个由逗号隔开的符号表,它们可能出现在stuff中
这里需要注意的是括号的左边一定要与name 紧邻,否则就有可能会被编译器误认为是stuff的一部分。
并且宏只是纯文本的替换,并不会计算 parament-list 里面的表达式。
看下面一段代码:
#define SQUARE( x ) x * x int a = 5; printf("%d\n" ,SQUARE( a + 1) );
预处理替换后:
int a = 5; printf("%d\n" ,5*5+1);
而不是:
int a = 5; printf("%d\n" ,36);
所以我们在使用宏时一定要多加括号,以免出现以上错误:
#define SQUARE( x ) ((x) * (x))
4.带有副作用的宏参数
当宏参数在宏的定义中出现超过⼀次的时候,如果参数带有副作用,那么你在使用这个宏的时候就可能出现危险,导致不可预测的后果。副作⽤就是表达式求值的时候出现的永久性效果。
看以下两个区别
x+ 1 ; // 不带副作⽤
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);
经过预处理后:
z = ( (x++) > (y++) ? (x++) : (y++));
我们可以看到x和y有可能出现两次自增。结果是
x=6 y=10 z=9
5.宏替换的规则
在程序中扩展#define定义符号和宏时,需要涉及几个步骤。
1. 在调用宏时,首先对参数进行检查,看看是否包含任何由#define定义的符号。如果是,它们首先被替换。
2. 替换文本随后被插入到程序中原来文本的位置。对于宏,参数名被他们的值所替换。
3. 最后,再次对结果文件进行扫描,看看它是否包含任何由#define定义的符号。如果是,就重复上述处理过程。
注意:
1. 宏参数和#define 定义中可以出现其他#define定义的符号。但是对于宏,不能出现递归。
2. 当预处理器搜索#define定义的符号的时候,字符串常量的内容并不被搜索。
6.宏函数的对比
宏通常被应用于执行简单的运算。
比如在两个数中找出较大的⼀个时,写成下面的宏,更有优势⼀些。
#define MAX(a, b) ((a)>(b)?(a):(b))
那为什么不用函数来完成这个任务?
原因有二:
1. 用于调用函数和从函数返回的代码可能比实际执行这个小型计算⼯作所需要的时间更多。所以宏比函数在程序的规模和速度方面更胜⼀筹。
2. 更为重要的是函数的参数必须声明为特定的类型。所以函数只能在类型合适的表达式上使用。反之这个宏怎可以适用于整形、长整型、浮点型等可以用于 > 来比较的类型。宏的参数是类型⽆关
的。
和函数相比宏的劣势:
1. 每次使用宏的时候,⼀份宏定义的代码将插⼊到程序中。除非宏比较短,否则可能大幅度增加程序的长度。
2. 宏是没法调试的。
3. 宏由于类型⽆关,也就不够严谨。
4. 宏可能会带来运算符优先级的问题,导致程容易出现错。
宏有时候可以做函数做不到的事情。比如:宏的参数可以出现类型,但是函数做不到。
#define MALLOC(num, type)\ (type )malloc(num sizeof(type)) ... //使⽤ MALLOC(10, int);//类型作为参数 //预处理器替换之后: (int *)malloc(10 sizeof(int));
宏和函数的⼀个对比: