C陷阱与缺陷——第六章:预处理器

在编译过程开始之前,C 语言预处理器首先对程序代码做了必要的转换处理。预处理器使得编程者简化某些工作,重要性体现在:(1)将出现在很多地方的某个数值显式的定义为一个显式常量,然后在程序需要的地方使用这个常量即可,当把所有的这类常量定义集中到一起,也方便管理。(2)宏定义一些程序块,看上去像是函数,减少函数调用时的系统开销。
宏只是对程序的文本起作用,即宏提供了一种对组成C 程序的字符进行变换的方式,而并不作用于程序中的对象。

6.1 不能忽视宏定义中的空格

注意宏定义中的空格,比如:
#define f (x) ((x)-1)
代表两种可能:f(x)代表((x)-1),或者 f 代表(x)((x)-1),在上述宏定义中,第二种是正确的,因为 f 和(x)之间多了一个空格,故正确的是:
#define f(x) ((x)-1)

6.2 宏并不是函数

宏从表面上看,其行为与函数相似,有时会引起编程者的迷惑,比如:# define abs(x) (((x)>=0)?(x):-(x)),或者# define max(a,b) ((a)>(b)?(a):(b)),这里的所有的括号,其作用只是预防引起与优先级有关的问题。
比如宏 abs(x) 被定义为 : #define abs(x) x>0?x:-x ,则在调用宏时,比如,abs(a-b);展开后,就出现了优先级引起的问题。故最好在宏定义中把每个参数都用括号括起来,同样,整个结果表达式也应该使用括号括起来,以防止当宏用于一个更大一些的表达式中可能出现的问题。
有时尽管宏定义中的各个参数与整个结果的表达式都被括起来,也仍然可能出现其它问题。比如说,一个操作数如果在两处都被用到,就会被求值两次。所以在定义宏时,尽量保证宏定义没有副作用,或者直接使用成函数。
使用宏的另一个潜在的风险是,宏展开可能造成非常庞大的表达式,占用的空间远超编程者所期望的空间。

6.3 宏并不是语句

编程者有时会试图定义宏的行为与语句类似,但这样做的实际上很困难,很难定义出符合编程者意愿的宏定义。比如 assert宏,它的参数是一个表达式,如果这个表达式为 0 ,就使程序终止,并给出一条适当的出错信息,把assert 作为宏处理,这就使得我们可以在程序出错信息中包括有文件名和断言失败处的行号,就assert(x>y); 在x 大于y 时,什么也不做,其他情况下则终止程序。该 assert 宏的最终定义是类似于一个表达式,而不是类似于一个语句:
即 #define assert(e) ((void)((e)||_assert_error(FILE,LINE)))
此种表达式,利用 || 运算符的短路特性,以及对其两侧的操作数依次顺序求值的性质。

6.4宏并不是类型定义

宏的另一个常见用途是,使多个不同变量的类型可以在一个地方说明: #define FOOTYPE struct foo
FOOTYPE a;
FOOTYPE b,c;
这样,编程者只需要在程序中改动一行代码,即可改变 a,b,c的类型,而与 a,b,c 在程序的什么地方声明无关,进而提高了程序的可移植性。
但是更通用的一般的方法还是使用类型定义: typedefine struct foo FOOTYPE;这个语句中,定义了 FOOTYPE 为一个新的类型,与struct foo 完全等效。
之所以说后者的类型定义的方法更加通用,考虑如下例子:
即 #define T1 struct foo *
typedefine struct foo *T2;
从两个定义上看,T1和T2从概念上是完全相同的,都是指向结构 foo 的指针。但是,当试图用他们声明多个变量时,就会出现问题:
T1 a,b;
T2 c,d;
第一个声明被扩展为:struct foo *a,b;这个语句中 a 被定为一个指向结构的指针,而 b 被定为一个结构 foo类型的数据。而第二个声明则不同,定义了 c 和 d 都是指向结构的指针,因为这里T2 的行为完全和一个真实的类型相同。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值