C语言宏

  本文主要讲解《C陷阱与缺陷》第六章中宏的使用。

  了解宏,我们首先得了解编译器编译C语言程序的过程:

C语言编译过程

其中预处理器工作有:

(1) 文件包含:可以把源程序中的#include 扩展为文件正文,即把包含的.h文件找到并展开到#include 所在处。
(2) 条件编译:预处理器根据#if和#ifdef等编译命令及其后的条件,将源程序中的某部分包含进来或排除在外。
(3) 宏展开:预处理器将源程序文件中出现的对宏的引用展开成相应的宏定义。

预处理中宏的作用:
(1)方便程序的修正:将某个特定数量在程序中出现的所有实例统统加以修改;

(2)提高程序的运行效率:C语言在实现函数调用时会带来重大系统开销,宏可以实现一种这样的程序块:它看上去像函数,但却没有函数调用的开销。

 

宏有有两种定义格式:
(1) 简单的宏定义:#define <宏名> <字符串>                                  //对应宏作用1

#define PI 3.1415926

#define PI 3.14
(2) 带参数的宏定义:#define <宏名> (<参数表>) <宏体>                //对应宏作用2

#define f(x) x

 

宏应该注意的地方:

(1)不能忽视宏定义中的空格:

  例子:下面宏定义中 f 是否带了一个参数?

#define f (x) ((x)-1)

 答案两种:f(x) = ((x) - 1) 或者 f=(x)  ((x)-1)

在上述定义中f = (x)  ((x) - 1)是正确的(虽然无法使用),因为 f 和后面的(x)之间多了一个空格。f (3) = 2;

因此在定义宏的时候,要严格的注重空格的使用,一般来讲带参数的宏,其参数列表与宏名要相连。

(很有趣:定义#define f(x) ((x) - 1), 调用f(3) = f (3) = 2)

 

(2)宏不是函数,需注重括号的使用:

例子:

#define abs(x) x > 0 ? x : -x //正确使用:#define abs(x) (((x)>0) ? (x) : (-x))

对abs(a-b)求值,宏被展开为:a-b >0 ? a-b : -a-b

例子中的-a-b相当于(-a)-b,而不是我们期望的-(a-b);因此,在定义宏的时候我们最好把每个参数都用括号括起来,整个表达式也括起来。

 

(3)宏不是类型定义:

看下面代码:

#define NEWTYPE struct newType
NEWTYPE a;
NEWTYPE b,c;

我们只需要改动一行代码,就可以改变a,b,c的类型,宏的这种用法有一个有点:可移植性,得到了所有C编译器的支持,但是,我们最好还是用typedef来定义:

typedef struct newType NEWTYPE

因为使用宏会带来意想不到的错误,例如:

#define T1 struct foo*
typedef struct foo *T2;

从定义上看,T1和T2的概念上完全符同,都是指向了foo的指针。但是,当我们试图使用它们来声明多个变量时,就会出现问题:

T1 a, b; //宏扩展:struct foo * a, b;
T2 a, b; //typedef定义:struct foo *a, *b;

可以看见使用宏扩展并不能得到我们想要的东西。

所以,使用类型定义的时候,我们最好不要使用宏,应该使用typedef。

 

对于宏的讲解结束,让我们回过头看看 宏应该注意的地方(1)

#define f (x) ((x)-1)

对于表达式 (x) ((x) -1) 是否能够成为一个合法的C表达式呢?

给出一种合理解:

我们定义:typedef int x;

在这种情况下:(x) ((x) - 1) 等价于 (int) ((int) - 1)

即将-1转换为int类型两次!

 

 

 

posted on 2012-08-18 11:15  asfion  阅读(2238)  评论(0编辑  收藏  举报

导航