在linux内核中有这样一个带参宏:container_of
1:内核原码:
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
#define container_of(ptr, type, member) ({\
2:container_of宏的作用:
如果在知道某个结构体成员地址的情况下,可以用container_of宏计算出该结构体的地址。
例如:
struct STU{
char name[10];
int age;
int num;
};
int main(void)
{
struct STU st1,*pst;
strcpy(st1.name,"xiaoming");
st1.age = 22;
st1.num = 100;
pst = container_of(&(st1.age),struct STU,age);
printf("%d\n",st1.age);
printf("%d\n",pst->age);
return 0;
}
上面的程序会输出两个22;说明pst通过container_of计算出了结构体st1的地址。
3:参数说明
(1):结构体成员的地址
(2):结构体类型
(3):第一个参数对应的成员名字
4:宏体解析
#define offsetof(TYPE, MEMBER) (
#define container_of(ptr, type, member) ({\
从内核原码中可以看出:除了container_of宏之外还有一个 offsetof(TYPE,MEMBER) 的带参宏,
从参数和具体实现来看,其替换的结果是 size_t(unsigned long)类型的数据,其实是用来计算偏移量的。
下面举例说明:
例如:
定义结构:
struct STU{
char name[10];
int age;
int num;
}st1;
int offset = offsetof(struct STUE,age);
offset 的值便是age成员的地址到结构体st1的地址的偏移量,其实很好理解,offsetof在这里做了个假设:
将0地址假设为结构体的起始地址,自然某个成员的地址便成了偏移量。有了偏移量的概念,接下来讨论container_of的实现:
container_of宏可以拆分成两部分:
(1):const typeof( ((type *)0)->member ) *__mptr = (ptr);
看起来挺复杂,其实就是在定义一个变量 __mptr;其类型为:( ((type *)0)->member ) *,
说白了就和 定义一个int 型的指针一样简单(其实这一句对整个功能实现来说,意义并不大,
再接下来会讨论到。
(2):(type *)( (char *)__mptr - offsetof(type,member) );
再看这句之前必须明白:指针的加减运算和其基类型有关!
有了前面偏移量的概念,只需要再有任意一个成员地址,通过指针的减法运算,便可以计算出结构体的地址了,
最后将其强转成需要的结构体类型便完成了最终的计算。
5:改写container_of
在之前说过const typeof( ((type *)0)->member ) *__mptr = (ptr);这一句意义不大,也不知为什么内核也一直没有修改过,这里仅仅是一些个人意见。
将container_of改写成如下结构同样可以实现功能:
#define container_of(ptr, type, member)
最后在贴出一个改写后的实例:
#include
#include
#define container_of(ptr,ty_pe,member)
struct STU{
};
int main(void)
{
return 0;
}