当前位置:首页 > 公众号精选 > 嵌入式云IOT技术圈
[导读]1 offsetof宏的原理以及作用 在使用container_of宏之前,我们先来了解下offsetof这个宏,它在Linux内核里的源码是这个样子: #define offsetof(TYPE,MEMBER) ((int) &((TYPE *)0)->MEMBER) 1.1 offsetof宏的工作原理 虚拟一个TYPE类型的结构体变量,通过TYPE.

















1 offsetof宏的原理以及作用

在使用container_of宏之前,我们先来了解下offsetof这个宏,它在Linux内核里的源码是这个样子:

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

1.1 offsetof宏的工作原理

虚拟一个TYPE类型的结构体变量,通过TYPE.MEMBER的方式来访问MEMBER成员,进而得到MEMBER成员相对于整个结构体首地址的偏移量。

这句话理解起来看似很抽象,&((TYPE *)0)->MEMBER相当于得到了成员的偏移减去0地址偏移,也就是结构体的首地址,进而就得到了该成员相当于整个结构体的偏移量,接下来写一个例子就明白了:

1.2 offsetof宏编程案例

#include <stdio.h>
#define offsetof(TYPE,MEMBER) ((int) &((TYPE *)0)->MEMBER)
#pragma pack(4)
struct ptr
{
char a ;
short b ;
int c ;
double d ;
};
#pragma pack()
int main(void)
{
struct ptr Pt ;
printf("ptr:%d\n",sizeof(struct ptr));
//相对地址偏移量
int offset = offsetof(struct ptr,a);
printf("offset:%d\n",offset);
int offset1 = offsetof(struct ptr,b);
printf("offset1:%d\n",offset1);
int offset2 = offsetof(struct ptr,c);
printf("offset2:%d\n",offset2);
int offset3 = offsetof(struct ptr,d);
printf("offset3:%d\n",offset3);
return 0 ;
}

运行结果:

在案例中,我们以默认4字节对齐得到的4个结构体变量在结构体中的偏移,明白了offsetof宏如何使用,就解决了我们的大疑问了,我们来看看container_of宏怎么使用吧。

2 Linux container_of宏的原理以及作用

Linux中的container_of宏长如下这个样子,那它有什么作用呢?我们来详细剖析一下。

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

2.1 container_of宏的工作原理

先用typeof获取变量的数据类型,也就是member成员的类型,然后将member这个成员 的指针转成自己类型的指针,再从offsetof相减,就得到整个结构体变量的首地址了,再将该地址强制转化为type *

接下来写一个关于container_of宏的编程案例:

2.2 container_of宏编程案例

#include <stdio.h>
#include <stdlib.h>
//获取结构体成员相对于结构体的偏移
#define offsetof(TYPE,MEMBER) ((int) &((TYPE *)0)->MEMBER)
//通过获取结构体中的某个成员的,反推该结构体的指针
#define container_of(ptr, type , member) ({ \
const typeof(((type *)0)->member) *__mptr = (ptr) ; \
(type *)((char *)__mptr - offsetof(type,member)) ;})
//让结构体默认以四字节对齐
#pragma pack(4)
struct ptr
{
char a ;
short b ;
int c ;
double d ;
};
#pragma pack()

int main(void)
{
struct ptr Pt ;
struct ptr *pt ;
printf("ptr:%d\n",sizeof(struct ptr));//16
//获取结构体的首地址
printf("ptr:%p\n",&Pt); //0028FEA8
Pt.a = 'a';
Pt.b = 2 ;
Pt.c = 4 ;
Pt.d = 12.04 ;
//通过container_of获取结构体的首地址
pt = container_of(&Pt.c, struct ptr , c);
printf("pt:%p\n",pt);
printf("a:%c\n",pt->a) ;
printf("b:%d\n",pt->b) ;
printf("c:%d\n",pt->c) ;
printf("d:%.2lf\n",pt->d);
return 0 ;
}

运行结果:

往期精彩

谈谈做产品、做项目以及标准化相关的话题

推荐一个非常好的项目管理工具

带串口屏显示的Bootloader

分享一个很好用的按键组件


若觉得本次分享的文章对您有帮助,随手点[在看]并转发分享,也是对我的支持。

免责声明:本文内容由21ic获得授权后发布,版权归原作者所有,本平台仅提供信息存储服务。文章仅代表作者个人观点,不代表本平台立场,如有问题,请联系我们,谢谢!

本站声明: 本文章由作者或相关机构授权发布,目的在于传递更多信息,并不代表本站赞同其观点,本站亦不保证或承诺内容真实性等。需要转载请联系该专栏作者,如若文章内容侵犯您的权益,请及时联系本站删除。
关闭
关闭