第六章 预处理器
来源:互联网 发布:小猪生活通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则是一个整型变量;而第二个声
明则是声明两个指针变量。
这是第六章。
- 第六章 预处理器
- 第六章 预处理器
- 第六章预处理
- 第六章 预处理、const、sizeof
- C陷阱与缺陷第六章 预处理器
- 程序员面试宝典第六章总结——预处理、const与sizeof
- 程序员面试宝典(第四版)——读书笔记-2、第六章:预处理、const与sizeof
- R语言学习第六天-----数据预处理
- 预处理器
- 预处理器
- 预处理器
- 预处理器
- 预处理器
- 预处理器
- 预处理器
- 预处理器
- 预处理器
- 预处理器
- 第三章 语义“陷阱”
- 随笔
- 第四章 连接
- 第五章 库函数
- execl自动填充
- 第六章 预处理器
- 第七、八章
- test
- 附录A
- C#解压RAR压缩文件
- 创静静态链接库过程
- 适配器模式(Adaptor)
- 持续集成之路——Maven(续)
- some link for network