linux 内核 container_of 接口

1. 原型

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

参数:
	1. ptr:指向一个成员的指针,一般是链表的指针(可以不是第一个成员,任意的)
	2. type:宿主结构体指针的类型,宿主结构体类型
	3. member:成员的名字
	
例子说明:
typedef struct tagTest{
	NODE_S *pNode; //一般情况下链表节点放结构体第一个(可以不是第一个)
	long i;
	long j;
	char k;
}TEST_S;

TEST_S *pTest = NULL;
NODE_S *pTmp  = xxx;

container_of(pTmp, typeof(*(pTest)), pNode)

2. 功能

其功能是实现了根据一个已知结构体某个成员的指针和这个成员的变量名得出宿主结构体的地址的功能

2.1. 例子-通俗易懂说container_of

//现在已知一个简单的结构体 TEST_S
typedef struct tagTest{
	NODE_S *pNode; //一般情况下链表节点放结构体第一个(可以不是第一个)
	long i;
	long j;
	char k;
}TEST_S;

TEST_S *pTest = NULL;
NODE_S *pTmp  = xxx;

现在已知链表 NODE_S *pTmp 的地址,需要求i的值,那么就需要知道 pTest 的地址才能求 i的值;
pTest = container_of(pTmp, TEST_S, pNode)
printf(pTest->i)
当然,一般情况比这个复杂
一般是 TEST_S 是一个复杂的结构体,其成员 i j k又都是另外一个复杂的结构体

3. 结合例子详细分析

#define container_of(ptr, type, member) ({ \
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type,member) );})
  1. ( (TYPE *)0 ) 将零转型为TYPE类型指针;
    就是上面例子中的TEST_S

  2. ((TYPE *)0)->MEMBER 访问结构中的数据成员;
    就是例子中的 pNode

  3. &( ( (TYPE *)0 )->MEMBER )取出数据成员的地址;
    就是例子中 的 pNode 的地址

  4. typeof( ((type*)0)->member ) 得到member的数据类型;
    就是例子中 的 pNode 的类型 NODE_S

  5. const typeof( ((type *)0)->member ) *__mptr = (ptr); //就是将指针变量__mptr 指向ptr;
    就是例子中 用__mptr 指向pNode的地址

  6. offset(type, member) 获得 member 在type中的相对偏移量;
    就是例子中 的 pNode 在TEST_S中的偏移量(这里是0,因为是第一个成员);

  7. (char *)__mptr - offset(type, member):将__mptr的地址 - type地址和member地址之间的差,其实也就是获取type的地址,ok结束
    就是例子中 用TEST_S的地址减去 pNode 的地址,

    为什么要把__mptr转换为char类型的指针呢?
    C语言中,一个指向特定数据类型的指针减1,实际上减去的是它所指向数据类型的字节大小sizeof(data),所以这里要把它转换成char类型,不然得不出正确结果

  8. (type*)( (char *)__mptr - offset(type, member) ) 最后把地址转换成宿主结构体的类型 type

4. 巧妙设计

  1. 巧妙之处在于将0转换成(TYPE*),结构以内存空间首地址0作为起始地址,则成员地址自然为偏移地址。
  2. offsetof 原型及原理
    #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
    &((TYPE *)0)->MEMBER获得MEMBER的地址
    因为type的地址为0,那么MEMBER自然为其在结构体中的偏移地址

5. 参考

1. https://blog.csdn.net/s2603898260/article/details/79371024
2. https://blog.csdn.net/wafx1314/article/details/17722893

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值