8.3 宏定义指令

宏定义指令是指用一些标识符作为宏名,来代替其他一些符号或者常量的预处理命令。使用宏定义指令,可以减少程序中字符串输入的工作量,而且可以提高程序的可移植性。

宏名既可以是字符串或常数,也可以是带参数的宏。宏定义指令可分为带参数的宏定义和不带参数的宏定义。下面分别介绍用于宏定义的一些预处理命令。

8.3.1 #define命令

define命令用于定义一个宏名。宏名是一个标识符,在源代码中遇到该标识符时,均以宏定义的串的内容代替该标识符。ANSI标准宏将定义的标识符称为“宏名”,而用定义的内容代替宏名的过程称为“宏替换”。#define命令用于定义宏名时,既可以带参数,也可以不带参数,下面分别介绍这两种情况。

1.不带参数的宏定义

经常使用的宏定义指令是不带参数的,这种用法最简单。不带参数的宏定义的一般形式如下。


define标识符字符串


其中,“#define”是宏定义指令,“标识符”即定义的宏名,“字符串”是宏替换的对象。最常用的宏定义是用字符代替数值,示例如下。


define PI 3.14159

define TURE 1

define FALSE 0


执行这些宏定义指令后,在源程序的编译过程中,若遇到PI就用3.1415926代替,遇到TURE就用1代替,遇到FALSE就用0代替。然后再进行编译连接。

为了提高程序可读性,在使用不带参数的宏定义指令时,应注意以下几点。

❑宏定义中的标识符与C51的标识符表示相同,为了与变量名相区别,一般使用大写字母定义标识符,这样便于发现宏替换的位置。

❑宏定义不是语句,所以不要在后面加分号。如果加了分号,程序编译时,会将分号作为代替的字符串的一部分一起进行宏替换。

❑在宏定义定义的标识符和字符串之间可以有任意个空格或制表符。

❑若有多个宏定义指令,应将其集中放置于源文件的开头,而不是分散放置在整个程序中。

❑如果宏定义指令较多,也可将其集中起来放在一个单独的文件中,然后用#include指令来包含该文件即可使用这些宏定义。

❑用宏定义指令定义的标识符可以替代任意的字符串,甚至是一些语句,示例如下。


define begin{

define end}


❑如果所要替换的字符串长于一行,为了便于程序的阅读,可以在该行末尾用一反斜杠“\”进行续行,示例如下。


define LONG_STR"this example show a very long string\

split to two line."


本例中,因为宏定义的宏名较长,书写、阅读起来都不太方便,所以使用反斜杠“\”进行续行。

❑宏定义的宏名可以嵌套使用。当宏名定义后,即可成为其他宏名定义中的一部分,示例如下。


define ONE 1

define TWO ONE+ONE

define THREE ONE+TWO


本例中先宏定义了宏名ONE来替代1,接着在宏定义TWO时,使用两个ONE的和来替代1+1=2。宏定义THREE时,采用相同的方法。

❑宏定义可以用来表示数组的大小,这样便于数组的修改,示例如下。


define SIZE 10

int a[SIZE];


本例中,先宏定义SIZE为10,然后声明了一个整型数组a,其大小由SIZE来指定。这样若要修改数组的大小,直接修改宏定义SIZE的大小即可。

❑在源程序中,用双引号引起来的字符串不发生宏替换,即宏定义对程序中字符串不起作用。程序示例如下。


include<stdio.h>//头文件

define STR“This is a test!\n”//宏定义字符串

void main()//主函数

{

printf(STR);//输出宏定义字符串

printf(“STR”);//输出字符串

}


该程序可以在KeilµVision3集成开发环境中运行,其结果如下。


This is a test!

STR


本例中,先宏定义STR替代字符串“This is a test!\n”,然后在主函数中遇到STR即用字符串的内容来宏替换,在第二个输出语句中的STR,因为被双引号引起来了,所以不发生宏替换。

2.带参数的宏定义

宏定义指令也可以带参数,带参数的宏定义指令的一般形式如下。


define标识符(参数表)字符串


其中,“#define”是宏定义指令,“标识符”即定义的宏名,“参数表”是宏定义的形参列表,字符串是宏替换的对象。

在C51程序中遇到宏名时,不仅要进行字符串的替换,与之相连的形参也要由程序中的实参替换,示例如下。


define FUN(A,B)2*A+B

y=FUN(1,7);


本例中,先宏定义了FUN,在程序中遇到FUN时,用括号中的实参1和实参7分别替换了宏定义的形参A和形参B,即上面的语句等同于表达式,如下所示。


y=2*1+7;


带参数的宏定义和函数在程序中的作用十分相似。用宏替换代替函数,可以增加程序代码的执行速度,因为使用宏替换不用经过函数调用的过程,但是由于重复编码,会使程序代码的最终长度增加。带参数宏定义的程序示例如下。


include<stdio.h>//头文件

define PI 3.14159//宏定义PI=3.14159

define L(R)2PIR//带参数的宏定义,用于计算周长

define S(R)PIRR//带参数的宏定义,用于计算面积

void main()//主函数

{

int r;

r=10;//半径

printf(“L(10)=%f\n”,L(r));//输出周长

printf(“S(10)=%f\n”,S(r));//输出面积

}


该程序可以在KeilµVision3集成开发环境中运行,其结果如下所示。


L(10)=62.831800

S(10)=314.159000


在本例中,先采用不带参数的宏定义,定义PI来替换数3.14159;接着采用带参数的宏定义,来定义圆的周长和面积的计算。在主程序中,分别调用这两个宏定义来求解半径为10的圆的周长和面积。