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

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

1. 宏不是函数

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

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

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

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

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宏

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

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);

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

3.宏并不是类型定义

#define T1 struct foo *
typedef struct foo * T2;

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

T1 a, b;
T2 a, b;

第一个声明被扩展为:

struct foo *a, b;

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

  • 目录 一.预处理的工作方式... 3 1.1.预处理的功能... 3 1.2预处理的工作方式... 3 二.预处理...
    朱森阅读 5,319评论 0 2
  • 注:这是第三遍读《C语言深度解剖》,想想好像自从大学开始就没读完过几本书,其中谭浩强的那本《C语言程序设计(第四版...
    HavenXie阅读 5,787评论 1 6
  • 我们的关系特别好,处于一种真正的暧昧状态。我知道你喜欢我,你不说出口,我也缄口不语。这种感觉就像是我们在吃棉花糖...
    居戎阅读 3,619评论 0 4
  • 1 插入排序 (把第一个当成有序其他当成无需) 希尔排序 2.冒泡排序 快速排序 3. 选择排序(一直找最小)
    雷一凡阅读 2,695评论 0 0
  • ,“天猫”(英文:Tmall,亦称淘宝商城、天猫商城)原名淘宝商城,是一个综合性购物网站。2012年1月11日上午...
    为小宝讲故事阅读 4,430评论 0 0

友情链接更多精彩内容