[关闭]
@yiltoncent 2015-11-16T14:10:16.000000Z 字数 1005 阅读 795

C语言预处理器之宏扩展 [C陷阱与缺陷]

C语言基础


C语言中宏是一个很好的工具,但也容易引起错误。

1. 宏不是函数

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

请注意宏定义中的括号,它们的作用就是预防引起与优先级有关的问题。没有这些括号,当宏展开的时候就可能引起意外的结果

  1. biggest = x[0];
  2. i = 1;
  3. while(i < n)
  4. biggest = max(biggest, x[i++]);

如果max是一个真正的函数,上面的代码可以正常工作,但是max如果是一个宏,那么就不能正常工作。展开一下:

  1. biggest = ((biggest)>(x[i++])?(biggest):(x[i++]));

首先变量biggest与x[i++]比较。假设此时i值为1,同时biggest小于x[1],那么关系运算结果为false。注意,因为i++有副作用,在比较后i递增为2,所以三目运算结果是x[2]。这明显不符合我们的预期,同时冒号后面的表达式还要经历一次副作用,整条语句结束后i的值是3。
而解决此类问题的一个办法是,确保max中的参数没有副作用

2. 宏不是语句

__FILE____LINE__是内建于C语言预处理器中的宏。

assert宏

  1. #define assert(e) if (!(e)) assert_error(__FILE__,__LINE__)

assert宏这个定义,即使用在一个再明白不过的情形中,也会有一些难以察觉的错误:

  1. if (x > 0 && y > 0)
  2. assert(x > y);
  3. else
  4. assert(y > x);

但是将其展开并左适当的缩排处理后,看一下:

  1. if (x > 0 && y > 0)
  2. if(! (x > y))
  3. assert_error("foo.c",37);
  4. else
  5. if(! (y > x))
  6. assert_error("foo.c",39);

因为C语言规定,else与它上面最相邻的if语句对齐。
所以这样的情况下,还是老实一点填写上括号比较好。

3.宏并不是类型定义

  1. #define T1 struct foo *
  2. typedef struct foo * T2;

从上面两个定义来看,T1和T2从概念上完全相同,都是指向结构foo的指针。但是,当我们试图用它们来声明多个变量时,问题来了:

  1. T1 a, b;
  2. T2 a, b;

第一个声明被扩展为:

  1. struct foo *a, b;

第二个声明则不同,它定义了a和b都是指向结构的指针,因为这里T2的行为完全与一个真实的类型相同。

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注