container_of 函数的分析

news2024/11/18 19:58:49

这个函数的目的是, 通过结构体里面的内容 找到 大结构体的 基地址。

函数的原型是: 

PTR是指针

type , member 都是具体的类型。

   12 /**
   11 ▎* container_of - cast a member of a structure out to the containing structure
   10 ▎* @ptr:    the pointer to the member.
    9 ▎* @type:   the type of the container struct this is embedded in.
    8 ▎* @member: the name of the member within the struct.
    7 ▎*
    6 ▎*/
    5 #define container_of(ptr, type, member) ({              \
    4 ▎   void *__mptr = (void *)(ptr);                   \
    3 ▎   BUILD_BUG_ON_MSG(!__same_type(*(ptr), ((type *)0)->member) &&   \
    2 ▎   ▎   ▎    !__same_type(*(ptr), void),            \
    1 ▎   ▎   ▎    "pointer type mismatch in container_of()");    \
  856 ▎   ((type *)(__mptr - offsetof(type, member))); })
    1
    2 /**

--------------------------------------------------------------------------------------------------------------------------

先来看一个 我自己的 追踪,

 接下来看一下  offsetof() 函数

结果是个这个。

再来追踪  __builtin_offsetof() 函数就追踪不到了,这是一个  GCC的函数。

从网上找找这个函数的实现。

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

接下来 解释 一下 这个函数。还是 又不少的东西的。

首先是 这个 : (TYPE *)0)->MEMBER

它的意思是 , 在 TYPE 这个结构体中, 找到 MEMBER成员, 但是 编译器 首先会找到 MEMBER的偏移地址, 并不会 对 0 地址的内存有什么操作 , 先。

然后是:   &((TYPE *)0)->MEMBER)   , 这意味着  我不是已经找到了MEMBER的的位置了吗, 现在 对这个位置 取地址,你知道,基地址是0 , 所以 MEMBER的地址,就是一个相对地址, 这样我实际上找到的是 TYPE 与MEMBER的差值。但是这个地址值,是有类型的,类型就是 MEMBER* 。

然后就是:   ((size_t) &((TYPE *)0)->MEMBER)    我把它强制转换成了一个 int 类型, 这就是一个数字了。

然后就是: __mptr - offsetof(type, member)))   这实际上就是 __mptr 减去一个 int 型的数字,

void *__mptr = (void *)(ptr)  这句说明, __mptr 是一个 void* 的指针。 那么   这句 __mptr - offsetof(type, member)))  就变成了  指针 加减 一个 整数了。

如果是在堆中的话,我们知道,堆是从下往上增长的。

那么  这个  ((size_t) &((TYPE *)0)->MEMBER)  将是一个正数。

那么  __mptr - offsetof(type, member)))  这个  意味着指针的位置 , 在从上往下 减, 也就是从一个小结构体, 找到了一个大结构体的 基地址。

然后就是:   ((type *)(__mptr - offsetof(type, member)));   这个函数的 type* 就是 在将 计算出的 大结构体的 指针 (这是一个数字), 转换成 大结构体指针类型,用于寻找 在这个大结构体 中的其他成员。

------------------------------------------------------------------------------------------------------------------------

来看看我自己的测试。

我是在 4412  arm 的裸机程序中做的测试。

 71 #define size_t unsigned int
 72 #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
 73
 74 struct human_mod{
 75
 76     int     head;
 77     char    eye;
 78     float   foot;
 79 };
 80
 81
 82 int main(void)
 83 {
 84         size_t ret;
 85         ret = offsetof(struct human_mod , foot);
 86         int i = 0;
 87         led_init ();
 88         while(1)
 89         {
 90
 91                 led_on(i%2);
 92                 led_off(((i-1)+2)%2);
 93                 i++;
 94                 delay_ms(500);
 95
 96         }
 97     return 0;
 98 }

这是 汇编的结果:


int main(void)
{
40008198:       e92d4800        push    {fp, lr}
4000819c:       e28db004        add     fp, sp, #4
400081a0:       e24dd008        sub     sp, sp, #8
/home/topeet/topeet_wang_4412_for_yikoulinux/yikoulinux_code/led_c/led.c:85
        size_t ret;
        ret = offsetof(struct human_mod , foot);
400081a4:       e3a03008        mov     r3, #8
400081a8:       e50b300c        str     r3, [fp, #-12]
/home/topeet/topeet_wang_4412_for_yikoulinux/yikoulinux_code/led_c/led.c:86
        int i = 0;
400081ac:       e3a03000        mov     r3, #0
400081b0:       e50b3008        str     r3, [fp, #-8]
/home/topeet/topeet_wang_4412_for_yikoulinux/yikoulinux_code/led_c/led.c:87
        led_init ();
400081b4:       ebffff95        bl      40008010 <led_init>
/home/topeet/topeet_wang_4412_for_yikoulinux/yikoulinux_code/led_c/led.c:91
        while(1)
        {

也就是说 ,汇编的是 已经是结果了,而不是 过程。

这里 直接 把 8 这个数字算出来了。 看来汇编代码 还不底层,更底层的应该是 编译器源码了。

400081a4:       e3a03008        mov     r3, #8

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2178805.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

PCL 快速均匀下采样

目录 一、概述 1.1原理 1.2实现步骤 1.3应用场景 二、代码实现 2.1关键函数 2.1.1 快速均匀下采样 2.1.2 可视化原始点云和下采样后的点云 2.2完整代码 三、实现效果 PCL点云算法汇总及实战案例汇总的目录地址链接&#xff1a; PCL点云算法与项目实战案例汇总&#…

恋爱辅助应用小程序app开发之广告策略

恋爱话术小程序带流量主广告开启&#xff0c;是一个有效的盈利模式&#xff0c;可以增加小程序的收入来源。以下是对此的详细分析 一、流量主广告的定义与优势 流量主广告是指在小程序中嵌入广告位&#xff0c;通过展示广告内容来获取广告主的付费。对于恋爱话术小程序而言&am…

图解C#高级教程(一):委托

什么是委托 可以认为委托是持有一个或多个方法的对象。但它与对象不同&#xff0c;因为委托可以被执行。当执行委托时&#xff0c;委托会执行它所“持有”的方法。先看一个完整的使用示例。 // See https://aka.ms/new-console-template for more informationdelegate void M…

无人机避障—— 激光雷达定高北醒TF03-UART(二)

无人机避障过程&#xff0c;光靠大疆飞控内部的气压计不准&#xff0c;很容易在高度较低的时候受到地面植被等障碍物影响&#xff0c;使得掉高严重&#xff0c;因此采用激光雷达定高模块进行定高。 硬件&#xff1a; 北醒TF03-UART、Xavier-NX 软件代码&#xff1a; 北醒官…

关于没有启用root问题,分区表挂载错误,导致系统无法启动

方法一、root没有登陆过&#xff0c;改root密码 1、为啥这样设置&#xff0c;root 2、密码破解也无效 2.1、开机启动&#xff0c;按 e 进入启动文件界面 2.2、把ro修改为rw&#xff0c;注意r和o之间包了个反斜杠 2.3、ctrl x退出当前模式 2.4、rw initsysroot/bin/sh 2.5、c…

HarmonyOs 查看官方文档使用弹窗

1. 学会查看官方文档 HarmonyOS跟上网上的视频学习一段时间后&#xff0c;基本也就入门了&#xff0c;但是有一些操作网上没有找到合适教学的视频&#xff0c;这时&#xff0c;大家就需要养成参考官方文档的习惯了&#xff0c;因为官方的开发文档是我们学习深度任何一门语言或…

http请求过程 part-2

http请求过程 http应用层 实体 实体分为实体首部和实体主体&#xff0c;实体首部是用来描述主体的 实体部分是可选的&#xff0c;它被用来运送请求或者响应的数据 传输层-TCP HTTP连接是建立在TCP连接的基础上 以流形式通过一条已经打开的TCP连接&#xff0c;按顺序进行…

Django Web开发接口定义

Django Web 介绍 Django Web是一个Pyhton高级 Web 框架,实际上 Django 也可以做到前后端分离,即主要作为后端框架使用,不用模板渲染也是可行的。 Django Web 应用的运行流程,如下图所示: 此外,Django Web 在开发环境可以通过自带的服务器进行本地调试。但是该服务器不适…

LeetCode从入门到超凡(五)深入浅出---位运算

引言 大家好&#xff0c;我是GISer Liu&#x1f601;&#xff0c;一名热爱AI技术的GIS开发者。本系列文章是我跟随DataWhale 2024年9月学习赛的LeetCode学习总结文档&#xff1b;本文主要讲解 位运算算法。&#x1f495;&#x1f495;&#x1f60a; 一、 位运算简介 1.什么是位…

【腾讯元宝-免费论文精读】

【腾讯元宝-免费论文精读】 1. 腾讯混元大模型2. 论文精读过程3. 总结&#xff1a; 1. 腾讯混元大模型 由腾讯研发的大语言模型&#xff0c;具备强大的中文创作能力&#xff0c; 复杂语境下的逻辑推理能力&#xff0c;以及可靠的任务执行能力 腾讯元宝&#xff1a;轻松工作&am…

Django对接支付宝沙箱环境(2024年9月新测有效)

1、申请沙箱环境 #需要填一些个人信息 https://opendocs.alipay.com/ 2、使用支付宝登入&#xff0c;并进入控制台&#xff0c;进入开发者工具推荐-->沙箱 3、获取基本信息 主要是APPID,和支付宝网关地址 4、生成应用私钥和应用公钥和支付宝公钥 上面的接口加签方式选择…

【Linux 22】生产者消费者模型

文章目录 &#x1f308; 一、生产者消费者模型⭐ 1. 生产者消费者模型的概念⭐ 2. 生产者消费者模型的特点⭐ 3. 生产者消费者模型的优点 &#x1f308; 二、基于阻塞队列的生产消费模型⭐ 1. 阻塞队列概念⭐ 2. 模拟实现基于阻塞队列的生产消费模型 &#x1f308; 三、POSIX 信…

Kubernetes云原生存储解决方案之 Rook Ceph实践探究

Kubernetes云原生存储解决方案之 Rook Ceph实践探究 除了手动部署独立的 Ceph 集群并配置与Kubernetes进行对接外&#xff0c;Rook Ceph 支持直接在 Kubernetes 集群上部署 Ceph 集群。 通过Rook Ceph云原生存储编排平台&#xff0c;使得 Kubernetes 集群中启用高可用的 Ceph…

PHP 基础语法详解

✅作者简介&#xff1a;2022年博客新星 第八。热爱国学的Java后端开发者&#xff0c;修心和技术同步精进。 &#x1f34e;个人主页&#xff1a;Java Fans的博客 &#x1f34a;个人信条&#xff1a;不迁怒&#xff0c;不贰过。小知识&#xff0c;大智慧。 &#x1f49e;当前专栏…

【有啥问啥】多目标跟踪SORT算法原理详解

多目标跟踪SORT算法原理详解 引言 多目标跟踪&#xff08;Multiple Object Tracking, MOT&#xff09;是计算机视觉领域的一个重要研究方向&#xff0c;广泛应用于视频监控、自动驾驶、人机交互等多个领域。其核心任务是在视频序列中持续、准确地识别和定位多个目标。SORT&am…

爬虫入门之爬虫原理以及请求响应

爬虫入门之爬虫原理以及请求响应 爬虫需要用到的库, 叫requests. 在导入requests库之前, 需要安装它, 打开cmd: 输入pip install 库名 pip install requests后面出现successful或requirement already就说明已经下载成功了!!! 下载出现的问题: 1.有报错或者是下载慢 修改镜像…

计算机的错误计算(一百零八)

摘要 回复网友来信&#xff0c;接前一节本节再谈多项式的错误计算。 例1. 计算 若在Visual Studio 2010中用C#编程计算&#xff1a; using System; using System.Collections.Generic; using System.Linq; class Program { static void Main(){ long part1 946495 * (…

Redis缓存双写一致性笔记(下)

Redis和Canal结合使用是一种常见的解决方案&#xff0c;用于确保MySQL数据库中的更改实时同步到Redis缓存中&#xff0c;从而保持数据的一致性。 这种同步机制虽然能够实现近乎实时的数据同步&#xff0c;但可能会有轻微的延迟&#xff0c;因此它更适合对数据一致性要求不是特…

STM32 DMA+AD多通道

单片机学习&#xff01; 目录 一、DMA配置步骤 二、ADC配置步骤 三、DMAAD多通道框图 四、DMAAD多通道函数设计详细步骤 4.1 开启RCC时钟 4.2 配置GPIO 4.3 配置多路开关 4.4 结构体初始化ADC 4.5 DMA参数初始化配置 4.5.1 外设站点的三个参数 4.5.2 存储器站点的三个…

Tomcat 调优技巧(Tomcat Tuning Tips)

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:Linux运维老纪的首页…