第六章 预处理器

来源:互联网 发布:小猪生活通9源码 编辑:程序博客网 时间:2024/06/07 02:04

 

今天花了点时间把第六章看了。

 

1.不能忽视宏定义中的空格。

 

         与宏调用相比,宏定义显得“暗藏机关”。例如下列代码:

 

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

 

         答案有两种:

 

         1.f(x)代表  ( (x) - 1 )

        

         2.f代表 (x) ( (x) - 1 )

 

         正确答案是第二种,因为f(x)之间多了一个空格!这一规则不适用于宏调用,只适用

 

于宏定义,也就是说,f(3)f (3)的结果是一样的。

 

2.宏并不是函数

 

         因为宏从表面上看上去其行为与函数很相似,所以程序员有时会情不自禁的把两者视为

 

等同。例如下列代码:

 

#define abs(x) ( (x) > 0 ? (x) : -(x) )

 

如果以abs(a-b)的形式调用并且参数没有被括号括起来就会发现问题了,因此为了避免出现

 

这类问题,需要将参数和结果都括起来。

 

         在宏定义中,如果一个参数被多次用到,那么这个参数将会被多次求值,因此,以下代

 

码会出现问题(部分代码):

 

#define max(a,b) ( (a) > (b) ? (a) : (b))

 

biggest = x[0];

 

i = 1;

 

while ( i < n )

         biggest= max( biggest, x[ i++ ]);

 

以上的代码不仅效率低下,而且还会出错。展开后会发现x[ i++ ]会被求值两次,从而导

 

致错误。如果max是函数则不会有这些问题。为了解决这类问题,我们需要确保宏定义中

 

的参数没有副作用,因此我们需要将while循环改为for循环。或者使用函数或代码。

 

         使用宏的另一个危险就是宏展开后可能产生非常庞大的表达式,占用空间远远超出了编

 

程者所预期的空间。同样是max的宏定义,如果对四个数进行比较max( a,max( b,max(c,d) ) );

 

你会发现展开的代码是在太长了!

 

3.宏不是语句

 

         有时,编程者会试图定义宏的行为与语句类似,但这样做会发现结果令人惊讶。

 

#define a(x) if(!x) assert_error(_FILE_,_LINE_)

 

if ( x>0 && y>0)

 

         a(x>y);

 

else

 

         a(y>x);

 

_FILE__LINE_是内建的。展开并整理代码后你会发现这是“悬垂”问题,有人就会说,那

 

我们在宏定义是加上括号,把句子括起来,那么你会发现另一个问题,else前面会多出一个

 

分号,这是一个语法错误。但是如果去掉代码中的分号你就会发现这代码会有一些“怪异”。

 

4.宏不是类型定义

 

         宏的另一个用途是使多个不同变量的类型可以在一个地方声明,相当于类型定义。如:

 

#define T int

 

T a;

 

T b,c;

 

这样子的做法有一个优点——可移植性。但是最好还是使用类型定义,因为这种做法也

 

是会出现问题的。

 

#define T1 int *

 

typedef int * T2;

 

T1 a,b;

 

T2 c,d;

 

         第一个声明展开后你会发现,a被声明为了指针,而b则是一个整型变量;而第二个声

 

明则是声明两个指针变量。

 

这是第六章。