• 由结构体成员地址计算结构体地址——list_entry()原理详解


    1. #define list_entry(ptr, type, member) container_of(ptr, type, member)
    在进行编程的时候,我们经常在知道结构体地址的情况下,寻找其中某个成员的地址;但是知道了成员的地址,如果找到这个结构体对应的地址呢?
    Linux内核中,获取节点地址的函数是list_entry(),它的宏定义如上所示。
    我们再来查找container_of(ptr, type, member)的定义,发现它依然是一个宏定义:
    1. #define container_of(ptr, type, member)
    2. ({
    3. consttypeof(((type *)0)->member)* __mptr =(ptr);
    4. (type *)((char*)__mptr - offsetof(type, member));
    5. })
    container_of(ptr, type, member)的宏定义中,真正返回节点地址的是最后一句话,
    而在最后一句话中offsetof(TYPE, MEMBER)依然是一个宏定义。
    1. #define offsetof(TYPE, MEMBER)((size_t)&((TYPE *)0)->MEMBER)
    1. typedef__kernel_size_tsize_t;
    2. typedefunsignedint__kernel_size_t;
    通过逐层查找之后我们来说一下list_entry()函数的具体实现,我们从下往上说起。
     
     
      1. #define offsetof(TYPE, MEMBER)((size_t)&((TYPE *)0)->MEMBER)
    • TYPE
    这是我们自定义的结构体类型,它的内部至少一个list_head型成员变量,如下:
    1. struct TYPR
    2. {
    3. //...
    4. struct list_head member;
    5. //...
    6. };
    其中list_head也是一个结构体,它的定义我们稍后再说。
    • MEMBER
    这是TYPE对象中list_head型变量的变量名。
    • 语句解析
    (TYPE *)0:将0强制转换成TYPE型指针,则该指针一定指向0地址(数据段基址)。
    &((TYPE *)0)->MEMBER这句话其实是&(((TYPE *)0)->MEMBER),通过该指针访问TYPE的MEMBER成员并得到其地址。
    由于该指针的起始地址是0,那么&((TYPE *)0)->MEMBER也就是一个TYPE型变量的起始地址
    与该变量内部MEMBER成员变量起始地址之间的偏移量,这个偏移量对于所有的TYPE型变量都是成立的。
    那么,接下来的思路便很明确了,我们只要知道一个TYPE类型变量中MEMBER变量的起始地址,减去
    offsetof(TYPE, MEMBER)这个偏移量,就可以得到TYPE类型变量的起始地址。
    它的的对应关系如下图所示:

     
    思路很清晰,但还有一些细节需要注意,我们继续看代码。
     
    2.
    1. #define container_of(ptr, type, member)
    2. ({
    3. consttypeof(((type *)0)->member)* __mptr =(ptr);
    4. (type *)((char*)__mptr - offsetof(type, member));
    5. })
    • const typeof(((type *)0)->member) * __mptr = (ptr);
    由于下面我们要对指针进行强制类型转换,所以这里我们又申请一个指针,指向和ptr相同的位置。
    这里的ptr指的是实际list_head member的地址。
    • (char *)__mptr
    由于offsetof()函数求得的是偏移字节数,所以这里(char *)__mptr使得指针的加减操作步长为1Byte
    然后二者相减便可以得到TYPE变量的起始地址,最后通过(type *)类型转换,将该地址转换为TYPE类型的指针。
    再向上就是一些宏定义没有什么可说的了。
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     





  • 相关阅读:
    codova 打包vue项目的坑
    vscode 开发wtl 笔记
    redis
    展开/收缩 ul
    ueditor
    xml
    NPOI
    滚动效果,有些浏览器不支持
    fileupload控件上传、文件下载
    excel函数
  • 原文地址:https://www.cnblogs.com/fengkang1008/p/4648484.html
Copyright © 2020-2023  润新知