C语言源程序中允许用一个标识符来表示一个字符串,称为“宏”。被定义为宏的标识符称为“宏名”。在编译预处理时,对程序中所有出现的宏名,都用宏定义中的字符串去代换,这称为宏替换或宏展开。
宏定义是由源程序中的宏定义命令完成的。宏替换是由预处理程序自动完成的。
在C语言中,宏定义分为有参数和无参数两种。下面分别讨论这两种宏的定义和调用。
1 .无参宏定义
无参宏的宏名后不带参数。其定义的一般形式为:
#define 标识符 字符串
#define 标识符 字符串
其中,“#”表示这是一条预处理命令(以#开头的均为预处理命令)。“define”为宏定义命令。“标识符”为符号常量,即宏名。“字符串”可以是常数、表达式、格式串等。
宏定义用宏名来表示一个字符串,在宏展开时又以该字符串取代宏名。这只是一种简单的文本替换,预处理程序对它不作任何检查。如有错误,只能在编译已被宏展开后的源程序时发现。
注意理解宏替换中“换”的概念,即在对相关命令或语句的含义和功能作具体分析之前就要进行文本替换。
【例1】定义常量:
#defineMAX_TIME 1000
若在程序里面写if(time < MAX_TIME){.........},则编译器在处理该代码前会将MAX_TIME替换为1000。
注意,这种情况下使用const定义常量可能更好,如const int MAX_TIME = 1000;。因为const常量有数据类型,而宏常量没有数据类型。编译器可以对前者进行类型安全检查,而对后者只进行简单的字符文本替换,没有类型安全检查,并且在字符替换时可能会产生意料不到的错误。
宏名一般用大写字母表示,以便于与变量区别。宏定义末尾不必加分号,否则连分号一并替换。宏定义可以嵌套。
可用#undef命令终止宏定义的作用域。
使用宏可提高程序通用性和易读性,减少不一致性,减少输入错误和便于修改。如数组大小常用宏定义。预处理是在编译之前的处理,而编译工作的任务之一就是语法检查,预处理不做语法检查。宏定义写在函数的花括号外边,作用域为其后的程序,通常在文件的最开头。字符串" "中永远不包含宏,否则该宏名当字符串处理。
宏定义不分配内存,变量定义分配内存。
2. 带参宏定义
C语言允许宏带有参数。在宏定义中的参数称为形式参数,在宏调用中的参数称为实际参数。
对带参数的宏,在调用中,不仅要宏展开,而且要用实参去代换形参。
带参宏定义的一般形式为:
#define 宏名(形参表) 字符串
#define 宏名(形参表) 字符串
在字符串中含有各个形参。
带参宏调用的一般形式为:
宏名(实参表);
在宏定义中的形参是标识符,而宏调用中的实参可以是表达式。
在带参宏定义中,形参不分配内存单元,因此不必作类型定义。而宏调用中的实参有具体的值,要用它们去代换形参,因此必须作类型说明,这点与函数不同。函数中形参和实参是两个不同的量,各有自己的作用域,调用时要把实参值赋予形参,进行“值传递”。而在带参宏中只是符号代换,不存在值传递问题。
【例3】
#define INC(x) x+1 //宏定义
y = INC(5); //宏调用
#define INC(x) x+1 //宏定义
y = INC(5); //宏调用
在宏调用时,用实参5去代替形参x,经预处理宏展开后的语句为y=5+1。