• 对offsetof、 container_of宏和结构体的理解


    1 offsetof 宏

     

    #include<stdio.h>

     

    #define offsetoff(type, member)      ((int)&((type*)0)->member)

     /* 

     ((type*)0)->member 释义:声明一个相应类型的结构体指针,该指针指向0地址处。再通过该指针访问各元素。我们只要获取一个指针就能访问其内部的元素吗,可以这么搞?其实我是想联系全局和非全局变量,通过上面这个代码也许你不明白我要表达的意思。请继续慢慢看,直到本文后面,结合本文后面对单链表的一个疑点及分析,你就会体会更深了。暂时这里先不管这个,

     

     指向0地址处的结构体指针访问各元素得到的地址值就是其偏移量了?  看来这是一个公认的道理。

     

     我不能解释为什么,但是我可以分析一下通过 '->"、 普通结构体指针访问其元素的性质,通过对比,

     如果性质相同,那就认了这个道理,不必深究。

     */

     typedef struct stu{

         char a;

         int b;

         char c;

     }*p1;

       

    int main(void)

    {

    struct stu stu1;

    stu1.b = 666;

    p1 p = &stu1;

    printf("&stu1 = %p. ", &stu1);

    printf("p = %p. ", p);   

    printf("stu1.b = %d. ", p->b);  

    printf("&(stu1.b) = %p. ", &(p->b));    

    }

    /*

    aston@ubuntu:/mnt/hgfs/shared$ gcc a.c

    aston@ubuntu:/mnt/hgfs/shared$ ./a.out

    &stu1 = 0xbfc485b4.

    p = 0xbfc485b4.       

    stu1.b = 666.

    &(stu1.b) = 0xbfc485b8.  //比上面多4个字节,合理。

     

    打印出来的都是0xbfc485b + x,

    如果直接先看这个例子,那我也很能理解各元素的地址打印出来将会是什么样,甚至都不会细想。想当然地认为:

    指针 + 结构体嘛,考虑下对齐,就知道各元素地址了。但是为什么一看见offsetoff就有点迷糊了呢?

    小结论:一个知识点只有在多种不同场景下被应用,才能加深印象。

     

    回到主问题:我们通过实验发现,结构体指针不同时,编译器都会自动增加偏移量。

     

    大结论: 我们通过“->”的方式访问结构体元素,其实是利用了编译器自动帮我们计算元素偏移量的特点。

             只有理解了这点,才能更好地理解 offsetof 宏。

     

    另外,这个例子我并没有产生“我们只要获取一个指针就能访问其内部的元素吗,可以这么搞”的疑惑,

    因为在这个例子里,stu1.b = 666;和printf("stu1.b = %d. ", p->b);这两句代码都在一个代码块{}中,

    我p->b访问结构体元素,p 和 b 都在同一个代码块{},当然可以访问啊。

    */

     

     

     

    再来看这个代码:

     

    #include<stdio.h>

    #include <stdlib.h>

    #include <strings.h>   

     

    typedef struct node{

        unsigned int data;   

        struct node* pNext;   //难道这个指针是全局的吗?

    }Node;  

     

    Node* creat_node(unsigned int data)

    {

        Node *p = (Node*)malloc(sizeof(Node));

        printf("sizeof(Node)-8- = %d. ", sizeof(Node));

        if(NULL ==p)

        {

           printf("malloc error");

           return NULL;

        }

       

        bzero(p,sizeof(Node));

        p->data = data;

        p->pNext = NULL;

        return p;

    }              

     

    int main(void)

    {

        Node* pheader= NULL; 

       

        pheader = creat_node(666);

       

        pheader->pNext = creat_node(777); 

       

        pheader->pNext->pNext = creat_node(888);

       

        printf("node1: data:%d ", pheader->data); 

    //creat_node的返回值只是p,但是却可以使用pNext这个指针(即访问p指向节点内的元素)。难道这个指针是全局的吗?data同样作为节点内的元素,data也是全局的?

        printf("node1: data:%d ", pheader->pNext->data);

        printf("node1: data:%d ", pheader-pNext-pNext->data);  

    }

     

    我们写个测试代码

    局部的结构体变量aaa。

    #include <stdio.h>

    typedef struct node{

        int data;

    }Node;

     

    void func(void)

    {

        Node aaa;

        aaa.data = 100;

    }

     

    int main(void)

    {  

        printf("%d ", aaa.data);

        return 0;

    }

    编译报错error: ‘aaa’ undeclared (first use in this function)

    测试结果:不是全局的。

     

    结论:我们只要获取了结构体的指针,我们就可以访问结构体内的元素(member).

    再结合这点理解文首的((type*)0)->member。哪怕你member是局部变量,我也通过结构体指针,就能获取你。

     再来证实一下:

    typedef struct node{

    int data;

    }Node;

    int func(void)
    {

    int addr = 0;
    Node aaa;

    addr = (int)&aaa;
    aaa.data = 100;

    return addr;

    }

    int main(void)

    {
    int a = func();


    printf("%d ", ((Node*)a)->data);

    return 0;

    }

     

     

    2 container_of宏

     

    #include <stdio.h>

    struct mystruct

    {

        char a;           // 0

        int b;        // 4

        short c;      // 8

    };

     

    // TYPE是结构体类型,MEMBER是结构体中一个元素的元素名

    // 这个宏返回的是member元素相对于整个结构体变量的首地址的偏移量,类型是int

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

     

    // ptr是指向结构体元素member的指针,type是结构体类型,member是结构体中一个元素的元素名

    // 这个宏返回的就是指向整个结构体变量的指针,类型是(type *)

        #define container_of(ptr, type, member) ({          

        const typeof(((type *)0)->member) * __mptr = (ptr); 

        (type *)((char *)__mptr - offsetof(type, member)); })      

     

    //我的下面两句代码不行。为什么?????????

    //#define container_of(ptr, type, member)     ((type*)((char*)ptr - offsetof(type, member));)

     

    //#define container_of(ptr, type, member)     ((type*)((int)ptr - offsetof(type, member));)

     

    int main(void)

    {

        struct mystruct s1;

        struct mystruct *pS = NULL;

       

        short *p = &(s1.c);      // p就是指向结构体中某个member的指针

       

        printf("  &s1 :%p. ", &s1);

       

        // 问题是要通过p来计算得到s1的指针

        pS = container_of(p, struct mystruct, c);

        printf("pS is :%p. ", pS);

          

        return 0;

    }

    .

    /************* 社会的有色眼光是:博士生、研究生、本科生、车间工人; 重点大学高材生、普通院校、二流院校、野鸡大学; 年薪百万、五十万、五万; 这些都只是帽子,可以失败千百次,但我和社会都觉得,人只要成功一次,就能换一顶帽子,只是社会看不见你之前的失败的帽子。 当然,换帽子决不是最终目的,走好自己的路就行。 杭州.大话西游 *******/
  • 相关阅读:
    SparkSql初级编程实践
    云时代架构之苏宁安全架构演进及实践
    云时代架构之知乎网站架构变迁史
    质量属性的六个常见属性场景之《淘宝网》
    云时代架构之游戏服务器的架构演进
    《架构漫谈阅读心得》
    转换后缀表达式
    约瑟夫环(改进3.0)
    栈结构之后缀表达式
    约瑟夫环(改进2.0)
  • 原文地址:https://www.cnblogs.com/happybirthdaytoyou/p/6879887.html
Copyright © 2020-2023  润新知