首页 > C > 阅读:57,774

宏定义

大家已经知道,C源程序需要经过编译和链接才能生成可执行文件。实际上,在C源程序进行编译的第一遍扫描(词法扫描和语法分析)之前,首先需要经历一个预处理阶段。预处理将由预处理程序负责完成。当编译器对一个源文件进行编译时,系统将自动引用预处理程序对源程序中的预处理部分作处理,处理完毕再进入对源程序的编译。

程序在编译预处理的阶段,不仅提供了宏定义的功能,还提供了文件包含以及条件编译的功能。文件包含在讲解“头文件”时已经提及,本章将详细介绍预处理中的“宏定义”,详细介绍预处理功能中的“条件编译”。

C语言源程序中,宏定义是指用一个标志符代表一个字符串,该标志符就称为宏名。在程序编译预处理阶段,所有宏名将会被替换为宏定义中的字符串,这个操作被成为宏展开。

宏定义分为不带参数的宏定义和带参数的宏定义,其定义格式如下:

不带参数格式:#define      标识符               字符串

带参数的格式:#define       标识符(参数表)          字符串


其中的标识符,我们称之为宏名。

13.1.1不带参数的宏定义

先来看不带参数的宏定义,比如在计算圆的面积的时候,经常使用PI的值。于是可以把PI的值利用宏定义来表示。

#define PI 3.14

float calccirclearea(float r)

{

       return PI*r*r;

}

那么,在进行预处理阶段,预处理程序会把代码中的宏名替换为3.14,即代码变为:

float calccirclearea(float r)

{

       return 3.14*r*r;

}

既然预处理是将PI替换为3.14,那么为什么不直接把3.14写进代码,这样就可以免去了预处理的时候进行替换呢?

首先,使用宏名PI在编写程序的时候代替3.14,这样可以提高程序的可读性,这是其一。其二,如果PI的值在程序中有多次引用,一旦PI的值需要在精度方面进行修改,那么使用了宏之后,只需要在宏定义的时候进行一次修改就可,而没有使用宏定义的时候,就需要在程序中多处修改。

13.1.2带参数宏

接下来再看带参数的宏的定义方法。比如,在程序里经常会遇到计算2个值中的最大值的算法问题。这个算法问题可以用宏定义如下:

#define MAX(X, Y) (X) > (Y) ? (X) : (Y)

void main(void)

{

       int a = 10;

       int b = 11;

       int c = MAX(a, b);

       printf(“max = %d\n”, c);

}

上面的程序在预处理阶段会被替换为下面的代码:

void main(void)

{

       int a = 10;

       int b = 11;

       int c =(a)>(b)?(a):(b);

       printf(“max = %d\n”, c);

}

 

上面分别介绍了用宏来定义一个常量和带参的宏定义的情况。但它们都只有一条语句。现在来看用宏定义一个多条语句的宏。在程序设计中,另外一个很经典的算法就是将两个数进行交换。比如有2个整数:

int a = 10;

int b = 20;

交换后,a的值为20b的值为10。在程序里面,必须要使用一个临时变量,来先把a的值保存起来,然后再把b的值赋给a,再把临时变量保存起来的a的值赋值给b。如果不用临时变量把a的值保存起来,那么当把b的值赋给a的时候,a的值就会丢失。

错误的写法如下:

a = b; //a的值丢失

b = a;//ab的值最终都是b的值

 

正确的写法:

int tmp = a; //使用tmp变量保存a的值

a = b;

b = tmp;

上面的算法可以用宏来定义如下:

 

#define SWAP(a,b)

{ \

       int tmp; \

       tmp = a; \

       a= b; \

       b = tmp; \

}

    上面的宏定义中,每行语句都用一个’\’来连接,用来将两个int类型的整数交换。然后在程序中使用这个宏定义如下:

 

void main(void)

{

       int a = 10;

       int b = 20;

       SWAP(a, b);

       printf(“a=%d, b=%d\n”, a, b);

}

    这种对于多条语句的定义方法,虽然在一般情况下不会出现什么问题。但是,来看看下面的条件语句:

If (x>y)

  SWAP(a, b);

else

  SWAP(a,b);

如果对上面的条件语句进行宏展开,就得到了下面的程序:

 

if (x>y)

{

       int tmp;

       tmp = a;

       a= b;

       b = tmp;

};//注意这里的分号,将if和else拆开了,else无法和if配对

else

{

       int tmp;

       tmp = a;

       a= b;

       b = tmp; 

};

很显然,else语句和if语句被分号;分割,出现一个语法错误,即else语句找不到if语句与之配对。

于是,当包含多条语句的宏,一般采用do-while(0)的格式来定义,即在语句外面加上一个do-while(0)来包装:

#define SWAP(a,b)

do { \

       int tmp; \

       tmp = a; \

       a= b; \

       b = tmp; \

}while(0)

这样,上面的程序展开之后:

if (x>y)

do{

       int tmp;

       tmp = a;

       a= b;

       b = tmp;

}while(0);

else

do {

       int tmp;

       tmp = a;

       a= b;

       b = tmp; 

}while(0);

这样do-while(0)本身就只是一条语句,宏定义中的语句也只执行一次就退出了do-while循环,也避免了上面宏定义出现的一个特殊错误。

周哥教IT,分享编程知识,提高编程技能,程序员的充电站。跟着周哥一起学习,每天都有进步。

通俗易懂,深入浅出,一篇文章只讲一个知识点。

当你决定关注「周哥教IT」,你已然超越了90%的程序员!

IT黄埔-周哥教IT技术交流QQ群:213774841,期待您的加入!

二维码
微信扫描二维码关注