1、预处理指令
本文主要介绍c语言中条件编译相关的预编译指令,包括 #define、#undef、#ifdef、#ifndef、#if、#elif、#else、#endif等
指令 | 功能 |
---|---|
#define | 定义一个预处理宏 |
#undef | 取消宏的定义 |
#if | 编译预处理中的条件命令,相当于C语法中的if语句 |
#ifdef | 判断某个宏是否被定义,若已定义,执行随后的语句 |
#ifndef | 与#ifdef相反,判断某个宏是否未被定义 |
#elif | 若#if, #ifdef, #ifndef或前面的#elif条件不满足,则执行#elif之后的语句,相当于C语法中的else-if |
#else | 与#if, #ifdef, #ifndef对应, 若这些条件不满足,则执行#else之后的语句,相当于C语法中的else |
#endif | #if, #ifdef, #ifndef这些条件命令的结束标志. |
defined | 与#if, #elif配合使用,判断某个宏是否被定义 |
1.1、#define、#undef
#define 机制包括了一个规定,允许把参数替换到文本中,这种实现通常称为宏(macro)或定义宏(define macro)
#define命令定义一个宏: #define MAX 100 之后写程序中MAX都代表着100,只要修改数字就可以修改程序中所有MAX的值
大体来说,宏定义,按照是否带参数通常分为对象宏、函数宏两种。
对象宏: 不带参数的宏被称为"对象宏(objectlike macro)"。对象宏多用于定义常量、通用标识。例如:
// 常量定义
#define MAX 100
// 通用标识,日志输出宏
#define SLog printf
// 预编译宏
#define _DEBUG
函数宏:带参数的宏。利用宏可以提高代码的运行效率: 子程序的调用需要压栈出栈, 这一过程如果过于频繁会耗费掉大量的CPU运算资源。 所以一些代码量小但运行频繁的代码如果采用带参数宏来实现会提高代码的运行效率。
// 最小值函数
#define MIN(a,b) ((a)>(b)? (a):(b))
// 安全释放内存函数
#define SAFE_DELETE(p) {if(NULL!=p){delete p; p = NULL;}}
#undef可以取消宏定义,与#define对应。
那我们看道题吧
#define add(x) (x) + (x)
printf ("%d\n",10 * (5) + (5));
这条语句将会输出什么呢?看上去,好像打印100,但事实上打印的是55.由于替换的原因那么就变成10*5+5=55,那我该怎修改的呢,加上括号改变运算优先级
#define add(x) ((x) + (x))
#define 替换规则
- 在调用宏时,首先对参数进行检查,看看是否包含任何由#define定义的符号。如果是,它们首先
被替换。- 替换文本随后被插入到程序中原来文本的位置。对于宏,参数名被他们的值所替换。
- 最后,再次对结果文件进行扫描,看看它是否包含任何由#define定义的符号。如果是,就重复上
述处理过程。
注意:- 宏参数和#define 定义中可以出现其他#define定义的符号。但是对于宏,不能出现递归。
- 当预处理器搜索#define定义的符号的时候,字符串常量的内容并不被搜索。
#和##
1.#,把一个宏参数变成对应的字符串。
2.##,可以把位于它两边的符号合成一个符号,它允许宏定义从分离的文本片段创建标识符。
如何把参数插入到字符串中?
inti=10;
#define PRINT(FORMAT, VALUE)
printf("the value of " #VALUE "is "FORMAT "\n", VALUE);
PRINT("%d", i+3);//产生了什么效果?
//最终的输出的结果应该是:
//the value of i+3 is 13
宏与函数对比:
就拿着MAX着宏与函数比较吧
#define MAX(a, b) ((a)>(b)?(a):(b))
谁更好?宏!
那为什么不用函数来完成这个任务?
原因有二:
- 用于调用函数和从函数返回的代码可能比实际执行这个小型计算工作所需要的时间更多。
所以宏比函数在程序的规模和速度方面更胜一筹。 - 更为重要的是函数的参数必须声明为特定的类型。
所以函数只能在类型合适的表达式上使用。反之这个宏怎可以适用于整形、长整型、浮点型等可以
用于>来比较的类型。
宏是类型无关的。
宏的缺点:当然和函数相比宏也有劣势的地方:
- 每次使用宏的时候,一份宏定义的代码将插入到程序中。除非宏比较短,否则可能大幅度增加程序
的长度。 - 宏是没法调试的。
- 宏由于类型无关,也就不够严谨。
- 宏可能会带来运算符优先级的问题,导致程容易出现错。
1.2 、#ifdef、#ifndef、#else、#endif
条件编译中相对常用的预编译指令。模式如下:
#ifdef ABC
// ... codes while definded ABC
#elif (CODE_VERSION > 2)
// ... codes while CODE_VERSION > 2
#else
// ... remained cases
#endif // #ifdef ABC
#ifdef用于判断某个宏是否定义,和#ifndef功能正好相反,二者仅支持判断单个宏是否已经定义,上面例子中二者可以互换。如果不需要多条件预编译的话,上面例子中的#elif和#else均可以不写。
1.3、#if、#elif、#else、#endif
#if可支持同时判断多个宏的存在,与常量表达式配合使用。常用格式如下:
#if 常量表达式1
// ... some codes
#elif 常量表达式2
// ... other codes
#elif 常量表达式3
// ...
...
#else
// ... statement
#endif
请看下面一个例子:
#include<stdio.h>
#define B
#ifdef A
#define str "hello world"
#elif B
#define str "hello CC"
#endifint main()
{
printf("%s\n",str);
return 0;
}
如果你觉得这个打印会是hello CC.那你就和我犯了一样的错误了。如果你用gcc -E hello.c -o hello.i 编译,(这条是预编译命令,下面会讲到。)会出现:error: #if with no expression的错误。原因是BB虽然定义了,但是定义的是空值,放在#elif后面就不行。因为#elif不仅仅是检查后面的宏有没有定义,还会检查其值。但是#ifdef就只是检查后面的宏是否定义,而不管其值为多少。读者可以把#define
BB改成#define AA试一下,结果就会打印hello world了。
读者如果有兴趣,也可以把#define BB改成#define BB 0 试一试,这时用gcc -E hello.c -o hello.i预编译可以编译通过,但是编译过程就不行了,因为#elif 0为假,HELLO没有定义。