Chinaunix首页 | 论坛 | 博客
  • 博客访问: 2003737
  • 博文数量: 369
  • 博客积分: 10093
  • 博客等级: 上将
  • 技术积分: 4271
  • 用 户 组: 普通用户
  • 注册时间: 2005-03-21 00:59
文章分类

全部博文(369)

文章存档

2013年(1)

2011年(2)

2010年(10)

2009年(16)

2008年(33)

2007年(146)

2006年(160)

2005年(1)

分类: C/C++

2008-05-01 11:54:33

在缺乏引用支持的C语言中,有的时候定义宏几乎成了唯一的选择,虽然有人认为引用代表了一种不怎么明确的语义:不知道参数是否可能会更改,但是如果引用施加于一个语义明确的对象,比如流,那么一切也就无可厚非了。

请看下面一段代码:


#include <stdint.h>
#include <inttypes.h>
#include <stdio.h>

#define get_u16(addr) ({ uint16_t **p = (uint16_t**)&(addr); *(*p)++; })

int main(int argc, char *argv[])
{
        uint16_t a[] = {12, 13};
        char *ptr = (char*)a;

        printf("% " PRIu16 "\n", get_u16(ptr));
        printf("% " PRIu16 "\n", get_u16(ptr));

        return 0;
}


get_u16的参数被当成了字节流的地址,每次调用它除了返回一个16比特的无符号整形外,数据流也会做相应的改变,可以认为get_u16吃掉并返回了16比特的无符号整形。上面代码运行正常:

gentux ~ # ./a.out
12
13

稍作改变,将ptr改成p,编译后运行:

xiaosuo@gentux ~ $ ./a.out
56984
56976

问题出现了,怎么会是这个结果呢?为了一探究竟我们不妨用cpp将宏展开来看:

int main(int argc, char *argv[])
{
 uint16_t a[] = {12, 13};
 char *p = (char*)a;

 printf("% " "u" "\n", ({ uint16_t **p = (uint16_t**)&(p); *(*p)++; }));
 printf("% " "u" "\n", ({ uint16_t **p = (uint16_t**)&(p); *(*p)++; }));

 return 0;
}


不难发现,本来应该是宏参数的变量被宏内部的变量覆盖了,根据C对作用域的定义,在宏内部用到的就是自己的变量,实际上我们应该庆幸,上面的代码没有断错误退出,多少给我们留足了面子。

问题的原因找到了,如何解呢?恕在下愚笨,着实没有找到完美的解决方法,只想到了借助编程惯例:除非是库,否则不应该定义以_开始的变量,这样的话,get_u16可以这样定义:

#define get_u16(addr) ({ uint16_t **_p = (uint16_t**)&(addr); *(*_p)++; })


结论:

因为宏毕竟不是函数,所以在其内部定义变量的时候要格外小心,最好在变量名前加上_,以防止覆盖外部变量,造成意外。
阅读(5003) | 评论(5) | 转发(0) |
0

上一篇:雨一直下

下一篇:阴雨五一

给主人留下些什么吧!~~

xiaosuo2008-05-06 22:46:57

被楼上发现了,呵呵。其实,Linux内核对GNU扩展的依赖很强,并且除了GCC,Intel的ICC也支持GNU扩展,在开源的世界里,我们几乎可以假设编译器支持GCC扩展。 上面的代码,如果想避开GCC扩展,还可以这样定义: #define def_get_type(type) \ inline type __get_##type(void *addr) \ { \ type **p = addr; \ return *(*p)++; \ } def_get_type(uint16_t); #define get_u16(x) __get_uint16_t(&(x))

mymtom2008-05-06 22:04:51

这种宏定义本来就是GNU扩展才支持的. 不建议使用,除非只准备用gcc而且对GNU扩展很熟悉.

BenBear2008-05-04 01:16:57

呃……不好意思,我看漏了一段话了……

xiaosuo2008-05-02 21:29:36

没错,就是ptr

BenBear2008-05-02 19:58:59

很好。但是……第一段代码中的 char *ptr = (char*)a; 是否应该是 char *p = (char*)a; ?