Chinaunix首页 | 论坛 | 博客
  • 博客访问: 188466
  • 博文数量: 28
  • 博客积分: 1490
  • 博客等级: 上尉
  • 技术积分: 310
  • 用 户 组: 普通用户
  • 注册时间: 2006-10-17 10:01
文章分类
文章存档

2012年(3)

2011年(2)

2008年(2)

2007年(7)

2006年(14)

我的朋友

分类: C/C++

2006-10-25 14:05:26


一、 来源于 linux kernel 的经典


1. 计算结构体中成员的偏移值

#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)

如果你知道结构体变量的地址,可以将该变量的地址加上偏移量就可以得到成员的地址,这通常没有什么意义,因为你可以根据 &(ptr_struct->member) 来获得,但如果要根据成员的地址来反求其宿主结构体的变量地址,这个宏就很重要了,也正是 offsetof(TYPE,MEMBER) 的价值体现,同时也是技巧的体现!

#include <stdio.h>
#include <assert.h>

#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)

typedef struct
{
    int a;
    char b;
    float c;
} MyStruct;

void
test_offsetof()
{
    MyStruct mystruct = { 111, 'c', 0.999};
    MyStruct *ptr = &mystruct;

    assert( (char*) &(mystruct.c) == (char*)ptr + offsetof(MyStruct, c) );

    float result = * ((float*)((char*)ptr + offsetof(MyStruct, c)));

    printf("mystruct.c = %f\n", result);
}


2. 根据成员的地址来反求其宿主结构体的变量地址

#define container_of(ptr, type, member) ({            \
        const typeof( ((type *)0)->member ) *__mptr = (ptr);    \
        (type *)( (char *)__mptr - offsetof(type,member) );})


在 2.4 的内核中是这样定义的:

#define container_of(ptr, type, member) ({            \
        (type *)( (char *)ptr - offsetof(type,member) );})

里面没有用的 typeof ──── GNU 的扩展来得到当前的成员的类型,这样就少了类型检测。


#include <stdio.h>
#include <assert.h>

#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
#define container_of(ptr, type, member) ({            \
        const typeof( ((type *)0)->member ) *__mptr = (ptr);    \
        (type *)( (char *)__mptr - offsetof(type,member) );})

typedef struct
{
    int a;
    char b;
    float c;
} MyStruct;

void
test_container_of()
{
    MyStruct mystruct = { 111, 'c', 0.999};
    MyStruct *p;

    float *ptr = &(mystruct.c);
    p = container_of(ptr, MyStruct, c);

    assert( &mystruct == p);
    printf("mystruct.a = %d\n", p->a);
}



3. do {} while(0)

将 do {} while (0) 用在宏里面是最恰当不过的了。

假设某一宏的定义如下:

#define TEST_MACRO(a, b, c) \
    a = c; b = c;


当程序这样来使用宏时:

if (condition)
    TEST_MACRO(a, b, c);


宏被替换后就变成了:

if (condition)
    a = c;
b = c;


显然,这与原来的意图相悖。很容易我们就通过更改以上的宏定义将其加上 {} 来达到目的。但这样也是不可取的。如:


#define TEST_MACRO(a, b, c) \
    {a = c; b = c;}

if (condition)
    TEST_MACRO(a, b, c);
else
    do_something_else();


宏展开后:

if (condition)
{
    a = c;
    b = c;
}
; /* <--- here !!! */
else
    do_something_else();


我想您已经看出来这里面的原因了。编译器是不会放过你的。

但,如果利用 do {} while(0) 定义的话,这些都可以避免了。

#define TEST_MACRO(a, b, c) \
    do { a = c; b = c; } while(0)



二、利用 GNU C 的扩展

简单说一下 typeof 的妙用,他能得到一个变量的类型。

通常的求两者的小值 MIN 宏定义都采用下面的方式:

#define MIN(a, b) \
    ((a) > (b) ? (b) : (a))


宏的定义者肯定希望传入的是相同类型的数字,但如果传入的不是一致的类型时,编译器是不会给出任何的警告信息的。这样会得到不是您预期的结果。比如:

printf("the min is: %d\n", MIN(100, 34.0));


要解决或者至少希望编译器能提醒您一下,我们可以采用这种方式:

#define MIN(a, b) ({\
    typeof(a) _a = a; \
    typeof(b) _b = b; \
    (void)(&_a == &_b);\
    _a > _b ? _b : _a })


(void)(&_a == &_b); 作用是检查参数 ab 的类型是否相同

关于 GNU 的扩展以及相关 typeof 的更多信息,
阅读(1788) | 评论(1) | 转发(0) |
给主人留下些什么吧!~~