搞懂它,就可以把结构体玩活了~
正文
大家周末好,我是bug菌~
今天主要是跟大家详细聊聊container_of这个宏定义,非常经典的宏,只是一直没有抽时间细细品味,今天就跟大家一起来看看有何神奇之处:
1
offsetof
首先我们需要简单看看offsetof(TYPE, MEMBER) 这个宏定义,它是用于计算一个结构体中某个成员的偏移量。
其第一个参数 TYPE 是一个结构体类型,第二个参数 MEMBER 是 TYPE 中的一个成员变量名。
它将返回类型为 size_t 的整数,表示 MEMBER 相对于 TYPE 起始地址的偏移量。
基本原理是根据 C 语言的数据对齐机制,成员变量在类型定义中的相对位置决定了它的偏移量。
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
该宏定义使用了C语言中的指针运算和类型转换。具体实现步骤如下:
1、(TYPE *)0:将0强制类型转换为指向类型为TYPE的指针,得到了一个结构体TYPE的空指针。
2、&((TYPE *)0)->MEMBER:求出结构体类型TYPE中成员MEMBER的地址。其巧妙之处在于,由于空指针不指向任何对象,因此这个成员的地址就是相对于结构体首地址的偏移量。
3、(size_t):将偏移量转换为无符号整型数,以满足C语言标准库中对offsetof()返回值的类型要求。
该宏定义可以在编译时就直接计算出偏移量,避免了运行时的计算开销,因此比通过变量名访问成员的方式更为高效,通常用在需要直接访问结构体成员的底层代码中,例如在操作系统内核、嵌入式系统以及一些高性能计算应用中。
struct TestStruct { int value1; char value2; double value3;
}; size_t offset = offsetof(struct TestStruct, value2);
如上例,offset 变量将会存储 value2 相对于 TestStruct 起始地址的偏移量。在这种情况下,因为 TestStruct 中的 value1 通常占用了 4 个字节,value2 占用了 1 个字节,所以 value2 相对于结构体起始地址的偏移量应该是 4。
2
container_of
讲完offsetof,来到今天的主角container_of,container_of()是一个在linux内核中经常使用的宏,用于获取一个结构体成员指针所在它所属的结构体的指针,有点绕口,细细品味。
该宏包括也主要包括三个参数:
ptr:结构体中某个成员的指针;
type:结构体类型名称;
member:结构体中ptr指向的成员名称。
首先,宏container_of()确定了ptr指向的成员在结构体中的偏移(offset)。通过offsetof()宏就可以得到这个偏移,其参数为结构体类型和成员名称。得到偏移后,再通过减去偏移的方式得到指向整个结构体的指针,巧妙吧。
具体实现如下:
#define container_of(ptr, type, member) ({ \const typeof(((type *)0)->member) *__mptr = (ptr); \(type *)((char *)__mptr - offsetof(type, member)); })
其中,typeof是GCC的一个扩展关键字,用于返回一个表达式的类型,可惜,大部分非GCC编译器不一定能支持。
假设ptr指向的成员变量的类型为T,__mptr就是一个指向T类型的指针。然后,调用offsetof()即可得到member在type类型中的偏移量,最后返回一个指向type类型的指针。
注意,尖括号不能省略,因为它表示类型转换。此外,container_of()宏使用了一个GCC的语言扩展"statement expression",即后面的{},可以在其中包含多条语句。
下面给出一个示例,用于说明container_of()的使用方法:
#include <stdio.h>
#include <stddef.h>#define container_of(ptr, type, member) ({ \const typeof(((type *)0)->member) *__mptr = (ptr); \(type *)((char *)__mptr - offsetof(type, member)); })struct student {int id;char name[20];
};int main() {struct student stu = {10001, "Zhang San"};char *pname = stu.name;struct student *pstu = container_of(pname, struct student, name);printf("ID: %d, Name: %s\n", pstu->id, pstu->name);return 0;
}
如上例,pname指向stu的name成员,通过container_of()宏获得了指向整个struct student结构体的指针pstu,然后就可以访问id和name成员了。
最后
好了,今天就跟大家分享这么多了,如果你觉得有所收获,一定记得点个赞+关注~
bug菌唯一、永久、免费分享嵌入式技术知识平台~
推荐专辑 点击蓝色字体即可跳转
☞ MCU进阶专辑
☞ 嵌入式C语言进阶专辑
☞ “bug说”专辑
☞ 专辑|Linux应用程序编程大全
☞ 专辑|学点网络知识
☞ 专辑|手撕C语言
☞ 专辑|手撕C++语言
☞ 专辑|经验分享
☞ 专辑|电能控制技术
☞ 专辑 | 从单片机到Linux
相关文章:

搞懂它,就可以把结构体玩活了~
正文 大家周末好,我是bug菌~ 今天主要是跟大家详细聊聊container_of这个宏定义,非常经典的宏,只是一直没有抽时间细细品味,今天就跟大家一起来看看有何神奇之处: 1 offsetof 首先我们需要简单看看offsetof(TYPE, MEMBER) 这个宏定…...

基于SpringBoot+Redis的前后端分离外卖项目-苍穹外卖(四)
编辑员工和分类模块功能开发 1. 编辑员工1.1 需求分析与设计1.1.1 产品原型1.1.2 接口设计 1.2 代码开发1.2.1 回显员工信息功能1.2.2 修改员工信息功能 1.3 功能测试 2. 分类模块功能开发2.1 需求分析与设计2.1.1 产品原型2.1.2 接口设计2.1.3 表设计 2.2 代码实现2.2.1 Mappe…...

dcat admin 各种问题
样式问题 如何根据条件给表格数据栏添加背景色 use Illuminate\Support\Collection;protected function grid(){return Grid::make(new BookArticle(), function (Grid $grid) {... 其他代码// Collection的完整路径:Illuminate\Support\Collection;$grid->row…...

数据结构与算法(二)动态规划(Java)
目录 一、简介1.1 什么是动态规划?1.2 动态规划的两种形式1)自顶向下的备忘录法(记忆化搜索法)2)自底向上的动态规划3)两种方法对比 1.3 动态规划的 3 大步骤 二、小试牛刀:钢条切割2.1 题目描述…...

颜值实力“C位出道”:起亚EV6综合实力究竟怎么样?
作为起亚电动化转型的标杆之作,起亚EV6已在全球赢得广泛赞誉,连续斩获“2022欧洲年度汽车”及“2023北美年度汽车”等多项国际大奖,其GT版本更是荣获“2023年度世界性能车”,这些荣誉不仅标志着其设计和技术的国际认可,…...

继承和多态_Java零基础手把手保姆级教程(超详细)
文章目录 Java零基础手把手保姆级教程_继承和多态(超详细)1. 继承1.1 继承的实现(掌握)1.2 继承的好处和弊端(理解) 2. 继承中的成员访问特点2.1 继承中变量的访问特点(掌握)2.2 sup…...
AI:85-基于深度学习的自然场景生成与渲染
🚀 本文选自专栏:人工智能领域200例教程专栏 从基础到实践,深入学习。无论你是初学者还是经验丰富的老手,对于本专栏案例和项目实践都有参考学习意义。 ✨✨✨ 每一个案例都附带有在本地跑过的代码,详细讲解供大家学习,希望可以帮到大家。欢迎订阅支持,正在不断更新中,…...

Windows电脑训练 RT-DETR 改进算法 (Ultralytics) 教程,改进RTDETR算法(包括使用训练、验证、推理教程)
手把手从零开始训练 RT-DETR 改进项目 (Ultralytics版本) 教程,改进RTDETR算法 本文以Windows服务器为例:从零开始使用Windows训练 RT-DETR 算法项目 《芒果剑指 RT-DETR 目标检测算法 改进》 适用于芒果专栏改进RT-DETR算法 文章目录 百度 RT-DETR 算法介绍改进网络代码汇…...

flask框架报错解决方法
1、报错 jinja2.exceptions.TemplateNotFound 解决方法:报错jinja2.exceptions.TemplateNotFound,template没找到,由于我之前直接将.html文件和.py文件直接放在同一目录下,经了解,需要新增一个 templates目录ÿ…...

Ubuntu18.04 安装docker教程
Ubuntu18.04 安装docker教程 1、前言 Docker Engine-Community 支持以下的 Ubuntu 版本: Xenial 16.04 (LTS)Bionic 18.04 (LTS)Cosmic 18.10Disco 19.04 Docker Engine-Community 支持以下CPU架构: x86_64(或 amd64)armhfarm…...
深入理解Git
目录 一、Git 的基本构造 1.1 关键对象类型 1.2 存储机制 二、Git 的内部工作 2.1 哈希和数据完整性 2.2 引用和可达性 2.3 分支和合并 2.4 垃圾回收 三、Git 高级特性 3.1 垃圾回收 3.2 钩子(Hooks) 3.3 子模块 四、常用命令 五、最佳实践…...

Leetcode_203.移除链表元素—C语言
目录 ❣️1.题目❣️ ❣️2.解答❣️ 💞方法一:暴力法 💞方法二: 尾插法 💞方法三:哨兵位法 ❣️1.题目❣️ 给你一个链表的头节点 head 和一个整数 val ,请你删除链表中所有满足 Node.va…...

虹科方案 | 汽车电子电气架构设计仿真解决方案
来源:虹科汽车电子 虹科方案 | 汽车电子电气架构设计仿真解决方案 导读 本文将介绍面向服务(SOA)的汽车TSN网络架构,并探讨RTaW-Pegase仿真与设计软件在TSN网络设计中的应用。通过RTaW将设计问题分解,我们可以更好地理…...
Java6种单例模式写法
单例模式 某个类任何情况下只有一个实例,并提供一个全局访问点来获取该实例。Java6种单例模式:2种懒汉式,2种饿汉式 ,静态内部类 ,枚举类懒汉式 synchronized延迟加载 public class Singleton {private static Sing…...
Direct3D拾取
假设在屏幕上单击,击中的位置为点s(x,y)。由图可以看出,用户选中了茶壶。但是仅给出点s,应用程序还无法立即判断出茶壶是否被选中。所以针对这类问题,我们需要采用一项称为“拾 取(Picking)”的技术。 茶壶和屏幕点s之间的一种联…...

大洋钻探系列之二IODP 342航次是干什么的?(上)
本文简单介绍一下大洋钻探IODP 342航次,从中,我们一窥大洋钻探航次的风采。 IODP342的航次报告在网络上可以下载,英文名字叫《Integrated Ocean Drilling ProgramExpedition 342 Preliminary Report》,航次研究的主要内容是纽芬兰…...

离散时间系统模型
离散时间系统模型 离散时间系统模型是表示数字滤波器的方案。MATLAB 科学计算环境支持若干种离散时间系统模型,这些模型将在以下章节中介绍: 传递函数零极点增益状态空间部分分式展开式(残差形式)二阶节 (SOS)格型结构体卷积矩…...

Nginx学习(在 Docker 中使用 Nginx)
1. 安装Nginx 使用 docker pull nginx 下载最新的 Nginx Docker 镜像。 下载完毕后,使用 docker run -d -p 80:80 --name nginx nginx,即可启动 Nginx 容器。其中,-p 80:80 表示将容器的 80 端口映射到 主机的 80 端口;--name ng…...

【Java】集合(一)单列集合List
1.集合 可以动态保存任意多个对象,并提供了一系列的操作对象的方法:add、remove、set、get等。 2.集合框架体系 分为两大类: 单列集合和双列集合 3.List接口基本介绍 List接口是Collection接口的子接口 List集合类中元素有序࿰…...

实战 | 基于卷积神经网络的蘑菇识别微信小程序
一个不知名大学生,江湖人称菜狗 original author: Jacky Li Email : 3435673055qq.com Time of completion:2023.11.13 Last edited: 2023.11.13 导读:其实没啥难的,主要是随手搞了就发出来把,太久没有水过帖子了&…...

19c补丁后oracle属主变化,导致不能识别磁盘组
补丁后服务器重启,数据库再次无法启动 ORA01017: invalid username/password; logon denied Oracle 19c 在打上 19.23 或以上补丁版本后,存在与用户组权限相关的问题。具体表现为,Oracle 实例的运行用户(oracle)和集…...

css实现圆环展示百分比,根据值动态展示所占比例
代码如下 <view class""><view class"circle-chart"><view v-if"!!num" class"pie-item" :style"{background: conic-gradient(var(--one-color) 0%,#E9E6F1 ${num}%),}"></view><view v-else …...
Objective-C常用命名规范总结
【OC】常用命名规范总结 文章目录 【OC】常用命名规范总结1.类名(Class Name)2.协议名(Protocol Name)3.方法名(Method Name)4.属性名(Property Name)5.局部变量/实例变量(Local / Instance Variables&…...

苍穹外卖--缓存菜品
1.问题说明 用户端小程序展示的菜品数据都是通过查询数据库获得,如果用户端访问量比较大,数据库访问压力随之增大 2.实现思路 通过Redis来缓存菜品数据,减少数据库查询操作。 缓存逻辑分析: ①每个分类下的菜品保持一份缓存数据…...

tree 树组件大数据卡顿问题优化
问题背景 项目中有用到树组件用来做文件目录,但是由于这个树组件的节点越来越多,导致页面在滚动这个树组件的时候浏览器就很容易卡死。这种问题基本上都是因为dom节点太多,导致的浏览器卡顿,这里很明显就需要用到虚拟列表的技术&…...

html css js网页制作成品——HTML+CSS榴莲商城网页设计(4页)附源码
目录 一、👨🎓网站题目 二、✍️网站描述 三、📚网站介绍 四、🌐网站效果 五、🪓 代码实现 🧱HTML 六、🥇 如何让学习不再盲目 七、🎁更多干货 一、👨…...
Go 并发编程基础:通道(Channel)的使用
在 Go 中,Channel 是 Goroutine 之间通信的核心机制。它提供了一个线程安全的通信方式,用于在多个 Goroutine 之间传递数据,从而实现高效的并发编程。 本章将介绍 Channel 的基本概念、用法、缓冲、关闭机制以及 select 的使用。 一、Channel…...

打手机检测算法AI智能分析网关V4守护公共/工业/医疗等多场景安全应用
一、方案背景 在现代生产与生活场景中,如工厂高危作业区、医院手术室、公共场景等,人员违规打手机的行为潜藏着巨大风险。传统依靠人工巡查的监管方式,存在效率低、覆盖面不足、判断主观性强等问题,难以满足对人员打手机行为精…...
第7篇:中间件全链路监控与 SQL 性能分析实践
7.1 章节导读 在构建数据库中间件的过程中,可观测性 和 性能分析 是保障系统稳定性与可维护性的核心能力。 特别是在复杂分布式场景中,必须做到: 🔍 追踪每一条 SQL 的生命周期(从入口到数据库执行)&#…...

MySQL:分区的基本使用
目录 一、什么是分区二、有什么作用三、分类四、创建分区五、删除分区 一、什么是分区 MySQL 分区(Partitioning)是一种将单张表的数据逻辑上拆分成多个物理部分的技术。这些物理部分(分区)可以独立存储、管理和优化,…...