Linux container_of宏详细剖析
扫描二维码
随时随地手机看文章
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获得授权后发布,版权归原作者所有,本平台仅提供信息存储服务。文章仅代表作者个人观点,不代表本平台立场,如有问题,请联系我们,谢谢!