A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

 找回密码
 加入黑马

QQ登录

只需一步,快速开始



从我们书写的C语言源代码,到一个可执行程序,所必须经历的四步:预处理、编译、汇编、链接。分别由预处理器、编译器、汇编器和链接器完成,但如今集成的IDE 环境,是把这些集成在一起完成的。如:VS、Qt等。但关于预处理器所做的事情,主要是头文件的替换和宏定义的替换,今天我们就来探究一下关于宏定义的事情。
1.    不能忽视宏定义中的空格
宏定义经常会带来歧义:如下面的额代码:
  
#define f  (x)  ((x) - 1)
  
答案只有两种,f(x) 代表((x)-1)或者f 代表(x)((x)-1)
在上述宏定义中,第二个答案是正确的,因为在f和后面的(x)之间呢多写了一个空格!所以,如果希望定义f(x)为((x)-1),应该写成这样:
  
#define f(x)  ((x)-1)
  
注意:这一规则不适用宏调用,只对宏定义使用,因此,上面完成宏定义后,f(3)和f   (3)求值后都等于2.
2.    宏并不是函数
宏从表面上看起来非常想函数,但需要注意,宏并不是函数。如:
想定义一个求绝对值的宏,定以如下:
  
#define abs(x)  x>0?x:-x
  
让我们来看abs(a-b)求值后的结果,表达式abs(a-b),展开为
  
a-b>0?a-b:-a-b
  
最终出现的结果和我们想要的结果完全不同,而正确的定义应该为:
  
#define abs(x)  (((x)  >=0) ? (x) : -(x))
  
此时abs(a-b)展开为:
  
((a-b)> 0 ?(a - b) : - (a-b))
  
这才是我们需要的答案,所以在定义宏函数时,为避免歧义,请尽可能的使用括号()。
3.    宏并不是语句
我们在写代码时,有时候会试图定义宏的行为与语句类似,但这样的实际困难往往令人吃惊。如:assert 宏,它的参数是一个表达式,如果该表达式为0,就使程序终止,并给出适当的错误信息。把assert作为宏来处理,这样就是的我们可以在出错信息中包含有文件名和断言失败处的行为,也就是说:assert(x > y);
在 X > Y 时什么也不做,其他情况终止。下面是我们定义的assert宏:
#define  assert(e) if (!e)assert_error(__FILE__,__LINE__)
因为宏assert的使用者会加上分号,所以在宏定义中并没有包含分号.__FILE__和__LINE__ 是内建于C语言的两个宏,会被扩展为所在的文件名和所处代码行的行号。
宏assert的定义,即使用在一个再明白不过的情形中,也会有一些难以察觉的错误:
if  (x > 0 && y> 0)
        assert(x > y);
else
        assert(y>x);
上面的写法看起来很合理,没有错误,但展开后就会发现错误:
If (x > 0 && y > 0)
        If(!(x >y))assert_error(“foo.c”,37);
else
        If(!(y > x))assert_error(“foo.c”,39);
如果这样看的还不清楚,可以对代码进行简单缩进;如:
If (x > 0 && y > 0)
        If(!(x >y))
assert_error(“foo.c”,37);
else
               If(!(y > x))
assert_error(“foo.c”,39);
这与我们想要表达的意思完全不同,故宏并不是语句,不要把它当成语句使用。
4.    宏并不是类型定义
宏的一个常见用途是,使多个不同变量的类型可在一个地方说明:
#define FOOTYPE  struct  foo
FOOTYPE a;
FOOTYPE b , c;
这样,编程者只需在程序中改动一行代码,即可改变a、b 、c 的类型,而与a、b、c在程序中什么地方声明无关。
宏定义的这种用法有一个优点--------可移植性,得到了所有C 编译器的支持。但是,我们最好还是使用类型定义:
typedef  struct foo  FOOTYPE;
这个语句定义了FOOTYPE 为一个新的类型,与struct foo完全等效。
这两种类型的方式似乎都差不多,但是使用typedef的方式更加通用,如,考虑下面的代码:
#define  T1  struct foo *
Typedef  struct  foo *T2
从上面两个定义来看,T1 和T2从概念上来看完全相同,都是指向结构foo 的指针,但是,当我们试图用他们来声明多个变量时,就出问题了:
T1 a,  b;
T2 a,  b;
第一个声明被扩展为;
struct  foo * a, b;
这个语句中a 被定义为一个指向结构的指针,而b 却被定义为一个结构(而不是指针),而第二个声明则不同,它定义了a 和 b都是指向结构的指针,因为T2的行为完全与一个真实的类型相同。
因此来说,宏并不是类型定义。
今天的讲解就到这里了,想要了解或者学习更多、更详细的内容,欢迎来到我们黑马程序员来咨询、学习。

精华推荐:

3分钟带你读懂C/C++学习路线
为什么来黑马程序员学C/C++? 稳做IT贵族人才!
2016最新C++学习路线图(附完整视频资源)+ 笔记 + 工具 + 面试题总结!

0 个回复

您需要登录后才可以回帖 登录 | 加入黑马