前言
为了能够真正的理解#define的作用,需要了解C/C++源程序的处理过程,在源程序的编译过程中实际经过了预处理、编译、汇编和链接几个过程。其中与宏相关的操作在预处理过程中完成,预处理包含以下几个过程:
(1)、文件包含
把源程序中#include包含的头文件扩展到文件正文,即把包含的.h文件找到并展开到#include所在的位置。
(2)、条件编译
预处理器根据#if和#ifdef等编译命令及其后的条件,将源程序中的某部分包含进来或排除在外,通常把排除在外的语句转换成空行。
(3)、宏展开
预处理器将源程序文件中出现的对宏的引用展开成相应的宏定义,即本文所说的#define的功能,由预处理器来完成。经过预处理器处理的源程序与之前的源程序有所不同,在这个阶段所进行的工作只是纯粹的替换与展开,没有任何计算功能,所以在学习#define命令时只要能真正理解这一点,才不会对此命令引起误解并误用。
1、宏定义
#define命令是C/C++语言中的一个宏定义命令,它用来将一个标识符定义为一个字符串,该标识符被称为宏名,被定义的字符串称为替换文本。该命令有两种格式:一种是不带参数的宏定义,另一种是带参数的宏定义。
一个标识符被宏定义后,该标识符便是一个宏名。程序中出现的宏名,在程序被编译前,先将宏名用被定义的字符串替换,称为宏替换,替换后才进行编译。宏替换只是简单的替换,即简单的纯文本替换,C/C++预处理器不对宏体做任何语法检查。
宏定义的基本格式如下所示:
(1)、不带参数的宏定义
不带参数的宏也被称为“对象宏(objectlike macro)”
#define经常用来定义常量,此时的宏名称一般为大写的字符串,这样利于修改这些常量。
#define <宏名> <字符串>
eg:
#define MAX 100
#define PI 3.14159
(2)、带参数的宏定义
带参数的宏也被称为“函数宏”,利用宏可以提高程序的运行效率。子程序的调用需要压栈出栈,这一过程如果过于频繁会耗费大量的CPU运算资源,所以一些代码量小但运行频繁的代码如果采用带参数的宏来实现会提高代码的运行效率。
#define <宏名>(<形式参数表>) <宏体>
函数宏的参数是固定的,函数宏的定义采用这种方式:#define name(args) tokens
其中的args和tokens都是可选的。
注意:name之后的左括号(必须紧跟name,之间不能有空格,否则其含义就是定义了一个对象宏,它将被替换为以(开始的字符串。但在调用函数宏时,name与(之间可以有空格。
eg:
#define A(x) x
#define MAX(a,b) ((a) > (b)) ? (a) : (b)
#define MUL(x,y) ((x)*(y))
(3)、取消宏定义
#undef <宏名>
eg:
#include <iostream>
using namespace std;
#define MAX 100
int main(int argc, char *argv[])
{
cout << MAX << endl;
#undef MAX
#define MAX 200
cout << MAX << endl;
return 0;
}
2、条件编译
2.1 #if,#elif,#else,#endif
#if,#elif,#else,#endif常用于条件编译,其形式如下:
#if 常量表达式1
语句1
#elif 常量表达式2
语句2
#elif 常量表达式3
语句3
...
#else
语句n
#endif
#if,#elif,#else分别相当于C/C++中的if,else以及else-if。
#if和#elif会计算它后面常量表达式的值,并检查常量表达式的结果,如果结果为真(true),就将其包含的代码包含到预处理后的文件中,如果结果为假(false),就将其包含的代码排除在外,使用这些条件编译命令可以非常方便的实现对源代码内容的控制。
#if后的常量表达式语法规则如下:
- 表达式中可以包含整型常量和宏,这个宏必须是被赋值过(区别被定义过)
- 可以使用圆括号来指定表达式计算的顺序
- 表达式中可以包含C/C++中的+,-,*,/,<<和>>算数运算符,也可以包含<,>,<=,>=和==关系运算符,以及&&和||逻辑运算符。
- 非(!)运算符可以反转表达式的结果,如: #if !(LIMXP > 12),如果LIMXP大于12,则表达式的结果为假。