当前位置: 首页 > news >正文

内核链表中offsetof 和container_of的一些理解

1,源码

struct home {int a;short b;char c;long f;
};#ifndef offsetof#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
#endif#ifndef container_of
#define container_of(ptr, type, member)					\({								\const __typeof__(((type *) NULL)->member) *__mptr = (ptr);	\(type *) ((char *) __mptr - offsetof(type, member));	\})
#endif

2 ,offsetof的理解

offsetof:type   —— 是最大结构体的类型,即:struct homemember —— 是其中的某个成员的名称,即 f所以求结构体成员f的偏移量可以这样使用:size_t len = offsetof( struct home, f);void show_per(struct home *p)
{printf("a: %u\n", (unsigned int)&p->a);printf("b: %u\n", (unsigned int)&p->b); 
}int offsetof_test()
{int len = 0;struct home per;memset( &per, 0, sizeof(struct home));per.a = 10;per.b = 11;per.c = 'A';per.f = 12;show_per(&per);show_per(NULL);len = offsetof( struct home, b);char *pp = &per;printf("len = %d\n", len);printf("b = %d\n", *(&per + len));printf("b = %d\n", *(pp + len));
}

 

解析:

 先看show_per,在调用这个函数中,传递了&per时,正确打印了a,b成员的地址,当我传递NULL时,按道理程序应该出问题的,但是却打印了0,4,这不就是偏移量么!!!

原来:

        在编译器中,结构体是存在地址对齐的,即空间为最小成员的最小整数倍,其次当我们使用->或‘.’访问成员时,编译器也是通过偏移量来访问成员变量的,如下:

从图中可以看出,当p为空时,首地址就是0,第二个成员为b(即:age),考虑字节对齐,所以第二个成员的偏移量就是4(int).

然后我们看最后3个打印: 4, 0 , 11.在第二个b = 0,跟我们的想法不一样也,我的想法是,最后两个b的值都是一样的为11,为什么?

 printf("b = %d\n", *(&per + len));

这个语句中取的per的地址,然后偏移len的长度,我们很自然想到首地址偏移4,不就是b成员的地址码,但是这里的per的类型是struct home,在它的基础上偏移4,就是移动了4*sizeof(struct home)的长度的地址,但这里并没有报错,打印为0,

解决方法:

        将这个地址值赋值给char *,然他一字节的移动,况且偏移量也是以字节为单位的。完美解决

 3,container_of的理解

container_of(ptr, type, member)	ptr    -- 结构体某个成员的地址type   -- 该结构体成员的类型member -- 结构体成员的名称作用:找某个成员所在的结构体的首地址//测试代码int container_of_test()
{struct home per;struct home *pper = NULL;memset( &per, 0, sizeof(struct home));per.a = 10;per.b = 11;per.c = 'A';per.f = 12;//赋值的是该成员的地址//long *p = NULL;//p = &per.f;char *p = &per.f;//这个宏计算出这个f成员所在结构体的首地址,赋值给pper,//所以我后面能够打印所有成员,p的地址一定要是其中某个成员本身的地址pper = (struct home *)container_of( p, struct home, f);printf("a = %d,b = %d, c = '%c'\n", pper->a, pper->b, pper->c);}

结果测试:

 从结果来看,确实通过container_of,打印了正确的成员变量的值。

container_of 第一行代码用来赋值和产生编译时结构体类型不匹配警告的,

第二行就是求首地址了:

        因为ptr的地址就是per.f的地址 — f成员在结构体中的offset ,那就得到了该结构体的首地址了。

所以后面我们可以正确打印这些成员。

4,总结

        介绍了offsetof 和 container_of 的使用和原理,在内核链表中,会有使用较多,方便以后查看

一下启发来自与这位大佬的博客:

Linux内核链表——看这一篇文章就够了 - Crystal_Guang - 博客园

相关文章:

  • Jackson 使用问题记录(持续更新)
  • 《Effective Python》第1章 Pythonic 思维总结——编写优雅、高效的 Python 代码
  • 数据结构(2)线性表-顺序表
  • 腾讯优化DeepSeek的DeepEP通信框架:开启AI大模型训练新时代
  • 运行Spark程序-在shell中运行 --SparkConf 和 SparkContext
  • 基于FPGA的视频接口之千兆网口(六GigE纯逻辑)
  • 深入理解BLP安全模型:信息安全中的“守密者”
  • 【力扣】K个一组翻转链表
  • 使用 百度云大模型平台 做 【提示词优化】
  • OpenMCU(七):STM32F103开发环境搭建
  • 如何选择与构建高效的网络流量分析平台
  • 【设备管理—磁盘调度算法】
  • 17.责任链模式:思考与解读
  • .Net HttpClient 处理错误与异常
  • Vue3+uniapp 封装axios
  • 计网实验笔记(一)CS144 Lab
  • 《猜拳游戏》
  • solidwors插件 开发————仙盟创梦IDE
  • uniapp开发4--实现耗时操作的加载动画效果
  • 按键精灵ios脚本新增元素功能助力辅助工具开发(三)
  • 国务院办公厅印发《国务院2025年度立法工作计划》
  • 北京航空航天大学首个海外创新研究院落户巴西
  • 学者的“好运气”:读本尼迪克特·安德森《椰壳碗外的人生》
  • 工人日报:“鼠标手”被纳入职业病,劳动保障网越织越密
  • 加强战略矿产出口全链条管控工作部署会召开
  • 匈牙利史专家阚思静逝世,享年87岁