linux内核中offsetof与container_of的宏定义
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
/**
* container_of - cast a member of a structure out to the containing structure
* @ptr: the pointer to the member.
* @type: the type of the container struct this is embedded in.
* @member: the name of the member within the struct.
* */
#define container_of(ptr, type, member) ({ \
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type,member) );})
offsetof含义:获取结构体中某个成员变量相对于结构体首地址的内存位置偏移;
container_of含义:根据结构体中某个成员变量的内存地址获取结构体的内存首地址。
offsetof宏详解:
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
1)、参数TYPE为结构体的类型定义,MEMBER为结构体成员变量名称;
2)、实现方法比较取巧:将0强制转化TYPE类型的指针,然后对MEMBER变量取址;
3)、代码示例:
typedef struct
{
int a;
char b;
void *c;
} off_struct_s;
printf("offset a:%d b:%d c:%d\n", offsetof(off_strcut_s, a), offsetof(off_strcut_s, b), offsetof(off_strcut_s, c));
最终结果为:offset a:0 b:4 c:8
container_of宏详解:
#define container_of(ptr, type, member) ({ \
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type,member) );})
1)、参数ptr为结构体成员的内存地址,type为结构体类型定义,member为结构体成员变量名称;
2)、typeof为gun编译器系列的内置函数,函数返回当前变量的类型;
3)、const typeof( ((type *)0)->member ) *__mptr = (ptr); 这行定义了一个member类型的指针变量__mptr,并且值初始化为ptr(注意:这里type也是0强制转换);
4)、(type *)( (char *)__mptr - offsetof(type,member) );
这行则是将结构体成员变量的内存地址减去此成员变量与结构体首地址的偏移(offsetof前面已讲解),即为结构体的内存首地址;
5)、代码示例:
off_struct_s stru;
void *ptr;
ptr = (void *)(container_of(&stru.c, off_struct_s, c));
printf("stru_addr:%p container_addr:%p\n", &stru, ptr);
运行结果必然是&stru与ptr的内存地址是一样的,这个例子只是做一个证明,实际使用时肯定是先不知道结构体首地址的,需要由结构体变量地址计算结构体首地址。
其实总结起来很简单,要想根据一个结构体变量的指针计算出结构体本身的指针,只需要当前变量指针减去变量到结构体首的偏移(base = ptr - offset)。