首页 > 代码库 > 对offsetof、 container_of宏和结构体的理解

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

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.\n", &stu1);

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

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

printf("&(stu1.b) = %p.\n", &(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.\n", 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.\n", sizeof(Node));

    if(NULL ==p)

    {

       printf("malloc error");

       return NULL;

    }

   

    bzero(p,sizeof(Node));

    p->data = http://www.mamicode.com/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\n", pheader->data); 

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

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

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

          

}

 

 

我们写个测试代码

局部的结构体变量aaa。

#include <stdio.h>

 

typedef struct node{

    int data;

}Node;

 

void func(void)

{

    Node aaa;

    aaa.data = http://www.mamicode.com/100;

}

 

int main(void)

{  

    printf("%d\n", 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 = http://www.mamicode.com/100;

return addr;

}

int main(void)

{
int a = func();


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

return 0;

}

 

 

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.\n", &s1);

   

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

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

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

      

    return 0;

}

 

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