container_of 函数的分析

这个函数的目的是, 通过结构体里面的内容 找到 大结构体的 基地址。

函数的原型是: 

PTR是指针

type , member 都是具体的类型。

   12 /**
   11 ▎* container_of - cast a member of a structure out to the containing structure
   10 ▎* @ptr:    the pointer to the member.
    9 ▎* @type:   the type of the container struct this is embedded in.
    8 ▎* @member: the name of the member within the struct.
    7 ▎*
    6 ▎*/
    5 #define container_of(ptr, type, member) ({              \
    4 ▎   void *__mptr = (void *)(ptr);                   \
    3 ▎   BUILD_BUG_ON_MSG(!__same_type(*(ptr), ((type *)0)->member) &&   \
    2 ▎   ▎   ▎    !__same_type(*(ptr), void),            \
    1 ▎   ▎   ▎    "pointer type mismatch in container_of()");    \
  856 ▎   ((type *)(__mptr - offsetof(type, member))); })
    1
    2 /**

--------------------------------------------------------------------------------------------------------------------------

先来看一个 我自己的 追踪,

 接下来看一下  offsetof() 函数

结果是个这个。

再来追踪  __builtin_offsetof() 函数就追踪不到了,这是一个  GCC的函数。

从网上找找这个函数的实现。

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

接下来 解释 一下 这个函数。还是 又不少的东西的。

首先是 这个 : (TYPE *)0)->MEMBER

它的意思是 , 在 TYPE 这个结构体中, 找到 MEMBER成员, 但是 编译器 首先会找到 MEMBER的偏移地址, 并不会 对 0 地址的内存有什么操作 , 先。

然后是:   &((TYPE *)0)->MEMBER)   , 这意味着  我不是已经找到了MEMBER的的位置了吗, 现在 对这个位置 取地址,你知道,基地址是0 , 所以 MEMBER的地址,就是一个相对地址, 这样我实际上找到的是 TYPE 与MEMBER的差值。但是这个地址值,是有类型的,类型就是 MEMBER* 。

然后就是:   ((size_t) &((TYPE *)0)->MEMBER)    我把它强制转换成了一个 int 类型, 这就是一个数字了。

然后就是: __mptr - offsetof(type, member)))   这实际上就是 __mptr 减去一个 int 型的数字,

void *__mptr = (void *)(ptr)  这句说明, __mptr 是一个 void* 的指针。 那么   这句 __mptr - offsetof(type, member)))  就变成了  指针 加减 一个 整数了。

如果是在堆中的话,我们知道,堆是从下往上增长的。

那么  这个  ((size_t) &((TYPE *)0)->MEMBER)  将是一个正数。

那么  __mptr - offsetof(type, member)))  这个  意味着指针的位置 , 在从上往下 减, 也就是从一个小结构体, 找到了一个大结构体的 基地址。

然后就是:   ((type *)(__mptr - offsetof(type, member)));   这个函数的 type* 就是 在将 计算出的 大结构体的 指针 (这是一个数字), 转换成 大结构体指针类型,用于寻找 在这个大结构体 中的其他成员。

------------------------------------------------------------------------------------------------------------------------

来看看我自己的测试。

我是在 4412  arm 的裸机程序中做的测试。

 71 #define size_t unsigned int
 72 #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
 73
 74 struct human_mod{
 75
 76     int     head;
 77     char    eye;
 78     float   foot;
 79 };
 80
 81
 82 int main(void)
 83 {
 84         size_t ret;
 85         ret = offsetof(struct human_mod , foot);
 86         int i = 0;
 87         led_init ();
 88         while(1)
 89         {
 90
 91                 led_on(i%2);
 92                 led_off(((i-1)+2)%2);
 93                 i++;
 94                 delay_ms(500);
 95
 96         }
 97     return 0;
 98 }

这是 汇编的结果:


int main(void)
{
40008198:       e92d4800        push    {fp, lr}
4000819c:       e28db004        add     fp, sp, #4
400081a0:       e24dd008        sub     sp, sp, #8
/home/topeet/topeet_wang_4412_for_yikoulinux/yikoulinux_code/led_c/led.c:85
        size_t ret;
        ret = offsetof(struct human_mod , foot);
400081a4:       e3a03008        mov     r3, #8
400081a8:       e50b300c        str     r3, [fp, #-12]
/home/topeet/topeet_wang_4412_for_yikoulinux/yikoulinux_code/led_c/led.c:86
        int i = 0;
400081ac:       e3a03000        mov     r3, #0
400081b0:       e50b3008        str     r3, [fp, #-8]
/home/topeet/topeet_wang_4412_for_yikoulinux/yikoulinux_code/led_c/led.c:87
        led_init ();
400081b4:       ebffff95        bl      40008010 <led_init>
/home/topeet/topeet_wang_4412_for_yikoulinux/yikoulinux_code/led_c/led.c:91
        while(1)
        {

也就是说 ,汇编的是 已经是结果了,而不是 过程。

这里 直接 把 8 这个数字算出来了。 看来汇编代码 还不底层,更底层的应该是 编译器源码了。

400081a4:       e3a03008        mov     r3, #8

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值