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

5. container_of 宏的定义、作用及手动实现详细解释

1. 宏的定义:

#define offsetof(type, member) ((size_t)&(((type *)0)->member))
#define container_of(ptr, type, member) \((type *)((char *)(ptr) - offsetof(type, member)))

2. 宏的作用:

container_of 宏用于通过一个结构体成员的指针,推算出整个结构体的首地址。在内核开发或系统编程中,通常通过链表节点、树节点等数据结构的成员来操作,而 container_of 宏可以通过这些成员的指针轻松获取包含该成员的整个结构体。

3. 宏的实现原理:

  • ptr: 指向结构体成员的指针。
  • type: 结构体的类型。
  • member: 结构体中的某个成员名。

实现的核心原理是利用指针运算和结构体成员的偏移量来获得结构体的起始地址。偏移量是通过标准的 offsetof 宏计算出来的,它返回的是结构体成员相对于结构体开头的字节数。

工作原理:

  • offsetof(type, member) 返回 member 相对于 type 结构体起始地址的偏移量(原理在手动实现中详细解释)。
  • (char *)(ptr) 将成员指针 ptr 转换为字节指针,以便按字节进行减法运算。
  • ((char *)(ptr) - offsetof(type, member)) 从成员的地址 ptr 中减去成员相对于结构体起始位置的偏移量,从而计算出整个结构体的首地址。
  • 最后,将结果转换为 type* 类型的指针,表示整个结构体的首地址。

4. 手动实现 container_of 宏并解释

为了手动实现 container_of 宏,我们需要依赖于结构体成员的地址以及其在结构体中的偏移量。我们可以不使用标准的 offsetof,而是手动计算成员的偏移量。以下是 container_of 宏的手动实现:

手动实现:

#define container_of(ptr, type, member) \((type *)((char *)(ptr) - ((size_t)&(((type *)0)->member))))

实现过程的详细解释:

4.1 ((type *)0)

这是将整数 0 转换为 type 类型的指针,也就是说,假设一个结构体的首地址为 0。我们实际上没有在内存中分配任何空间,这仅仅是一个假设操作。该操作的作用是用于后续计算结构体成员的偏移量。

(type *)0

4.2 (((type *)0)->member)

通过 ((type *)0) 得到一个指向结构体类型为 type 且地址为 0 的假设结构体。接着通过 ->member 访问该假设结构体的 member 成员。因为结构体假设位于内存地址 0,此时 member 的地址就是其相对于结构体开头的偏移量。

&(((type *)0)->member)

这一步将返回 member 成员相对于结构体首地址的偏移量,并且返回的是指针形式。我们通过 & 符号取 member 成员的地址。

4.3 (size_t)&(((type *)0)->member)

这一行将上一步获得的 member 成员的地址(实际上是相对偏移量)转换为 size_t 类型。size_t 是无符号整数类型,可以存储偏移量。

4.4 (char *)(ptr)

将传入的 ptr(指向 member 成员的指针)转换为 char* 类型指针。这样做的原因是 char* 类型指针按字节处理,这使得我们可以以字节为单位进行地址操作。

4.5 ((char *)(ptr) - ((size_t)&(((type *)0)->member)))

这一步是核心操作。通过将成员指针 ptr 减去成员 member 在结构体中的偏移量,来计算结构体的首地址。这样,我们就可以从成员指针 ptr 中推算出包含该成员的结构体的起始地址。

4.6 (type *)(...)

最后,将计算出的结构体首地址转换为 type* 类型的指针,即指向整个结构体的指针。

示例代码:

#include <stdio.h>struct MyStruct {int a;float b;char c;
};#define container_of(ptr, type, member) \((type *)((char *)(ptr) - ((size_t)&(((type *)0)->member))))int main() {struct MyStruct s = {1, 2.5, 'A'};// 获取成员 c 的指针char *ptr_to_c = &s.c;// 使用 container_of 获取结构体的首地址struct MyStruct *struct_ptr = container_of(ptr_to_c, struct MyStruct, c);// 验证计算出的地址是否正确printf("Original struct address: %p \n", (void*)&s);printf("Calculated struct address: %p \n", (void*)struct_ptr);return 0;
}

运行结果:

Original struct address: 0x7ffee58fa8f0
Calculated struct address: 0x7ffee58fa8f0

总结:

  • container_of 宏通过成员指针 ptr 计算出结构体的起始地址。
  • 计算的关键步骤是利用成员的偏移量,通过指针减法推算结构体的首地址。
  • 手动实现中,通过创建一个虚拟的结构体(位于地址 0)来获取成员的偏移量。


http://www.mrgr.cn/news/3793.html

相关文章:

  • pyqt 用lamada关联信号 传递参数 循环
  • DNF攻略:护石符文体系辅助详解,VMOS云手机助攻核心玩法!
  • macOS symbol(s) not found for architecture arm64错误原因总结
  • 理解 CSS 层叠、优先级和继承——WEB开发系列15
  • springboot jar是如何启动的
  • select模型实现TCP聊天室
  • 连锁店收银系统源码-线下收银多端视频展示
  • GPT-4o微调功能现已上线
  • Redis十大数据类型
  • 【HTML】弹性盒子 (display: flex) 布局
  • web前端之选项卡的实现、动态添加类名、动态移除类名、动态添加样式、激活、tabBar
  • 南大-ICS2021 PA1~PA2.2 学习笔记记录
  • 计算机毕业设计Python深度学习房价预测 房价可视化 链家爬虫 房源爬虫 房源可视化 卷积神经网络 大数据毕业设计 机器学习 人工智能 AI
  • 如何生成随机数(通过rand函数,srand函数,time函数深入讲解)
  • BaseCTF 高校联合新生赛Week1(web)
  • Open3D mesh 模型切片
  • 小琳AI课堂:AI(人工智能)和AIGC(AI生成内容)的关系
  • Array List集合的基本使用
  • Docker使用教程
  • 下拉表格选择器ReTableSelect组件(API)