一、 来源于 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);
作用是检查参数 a 和 b 的类型是否相同。
关于 GNU 的扩展以及相关 typeof 的更多信息,
阅读(1812) | 评论(1) | 转发(0) |