container_of 函数的分析

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

函数的原型是: 

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.mfbz.cn/a/885063.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

新手上路:Anaconda虚拟环境创建和配置以使用PyTorch和DGL

文章目录 前言步骤 1: 安装 Anaconda步骤 2: 创建新的 Anaconda 环境步骤 3: 安装最新版本的 PyTorch步骤 4: 安装特定版本的 PyTorch步骤 5: 安装最新版本的 DGL步骤 6: 安装特定版本的 DGL步骤 7: Pycharm中使用虚拟环境解释器第一种情况&#xff1a;创建新项目第二种情况&am…

Word办公自动化的一些方法

1.Word部分内容介绍 word本身是带有格式的一种文档&#xff0c;有人说它本质是XML&#xff0c;所以一定要充分利用标记了【样式】的特性来迅速调整【格式】&#xff0c;从而专心编辑文档内容本身。 样式&#xff08;集&#xff09; 编号&#xff08;多级关联样式编号&#xff…

Tomcat系列漏洞复现

CVE-2017-12615——Tomcat put⽅法任意⽂件写⼊漏洞 漏洞描述 当 Tomcat运⾏在Windows操作系统时&#xff0c;且启⽤了HTTP PUT请求⽅法&#xff08;例如&#xff0c;将 readonly初始化参数由默认值设置为false&#xff09;&#xff0c;攻击者将有可能可通过精⼼构造的攻击请求…

探索 Snowflake 与 Databend 的云原生数仓技术与应用实践 | Data Infra NO.21 回顾

上周六&#xff0c;第二十一期「Data Infra 研究社」在线上与大家相见。活动邀请到了西门子数据分析师陈砚林与 Databend 联合创始人王吟&#xff0c;为我们带来了一场关于 Snowflake 和 Databend 的技术探索。Snowflake&#xff0c;这个市值曾超过 700 亿美元的云原生数据仓库…

Android 安卓内存安全漏洞数量大幅下降的原因

谷歌决定使用内存安全的编程语言 Rust 向 Android 代码库中写入新代码&#xff0c;尽管旧代码&#xff08;用 C/C 编写&#xff09;没有被重写&#xff0c;但内存安全漏洞却大幅减少。 Android 代码库中每年发现的内存安全漏洞数量&#xff08;来源&#xff1a;谷歌&#xff09…

资质申请中常见的错误有哪些?

在申请建筑资质的过程中&#xff0c;企业可能会犯一些常见的错误&#xff0c;以下是一些需要避免的错误&#xff1a; 1. 资料准备不充分&#xff1a; 申请资质需要提交大量的资料&#xff0c;包括企业法人资料、财务报表、业绩证明等。资料不齐全或不准确都可能导致申请失败。…

汽车线束之故障诊断方案-TDR测试

当前&#xff0c;在汽车布局中的线束的性能要求越来越高。无法通过简单的通断测试就能满足性能传输要求。早起对智能化要求不高&#xff0c;比如没有激动雷达、高清摄像、中央CPU等。 近几年的智能驾驶对网络传输要求越来越高&#xff0c;不但是高速率&#xff0c;还需要高稳定…

【C++题目】7.双指针_和为 s 的两个数字

文章目录 题目链接&#xff1a;题目描述&#xff1a;解法C 算法代码&#xff1a;图解 题目链接&#xff1a; LCR 179.查找总价格为目标值的两个商品 题目描述&#xff1a; 解法 解法一&#xff08;暴力解法&#xff0c;会超时&#xff09; 两层 for 循环列出所有两个数字的组合…

一种使用 SUMO + Python 联合仿真平台

一种使用 SUMO Python 联合仿真平台&#xff08;一&#xff09; 本文适用人群包括但不仅限于【交通运输】【车辆工程】【自动化控制】【计算机科学与技术】等专业本科生、研究生、博士生。本文通过在Pycharm平台&#xff0c;使用Python语言 Traci工具包&#xff0c;调用SUMO客…

【步联科技身份证】 身份证读取与解析———未来之窗行业应用跨平台架构

一、身份证解析代码 C# function 身份证数据解析_湖南步联科技(wzxx) {var result {};result[xm] wzxx.substr(0, 15);result[xbdm] wzxx.substr(15, 1);result[mzdm] wzxx.substr(16, 2);result[csrq] wzxx.substr(18, 8);result[dzmc] wzxx.substr(26, 35);result[gms…

Ansible-template模块动态生成特定文件

文章目录 一、Jinja2介绍什么是主要特性安装基本用法进阶特性总结 Jinja2与Ansible关系1. 模板引擎2. Ansible 的依赖3. 变量和模板4. 动态生成配置5. 社区和生态系统总结 二、Ansible如何使用Jinja2使用template模块Jinja2文件中使用判断和循环Jinja2文件中使用判断语法 Jinja…

如何在算家云搭建text-generation-webui(文本生成)

一、text-generation-webui 简介 text-generation-webui 是一个流行的用于文本生成的 Gradio Web UI。支持 transformers、GPTQ、AWQ、EXL2、llama.cpp (GGUF)、Llama 模型。 它的特点如下&#xff0c; 3 种界面模式&#xff1a;default (two columns), notebook, chat支持多…

Vue发送邮件攻略:从搭建到实现详细步骤?

vue发送邮件功能实现方法&#xff1f;Vue前端如何实现发送邮件&#xff1f; 随着应用功能的不断扩展&#xff0c;用户交互的复杂性也在增加。其中&#xff0c;发送邮件功能是许多Web应用中不可或缺的一部分。AokSend将详细介绍如何使用Vue.js实现发送邮件功能。 Vue发送邮件&…

Springboot指定扫描路径

方式一&#xff1a;通过在启动类的SpringbootApplication中指定包扫描或类扫描 指定需要扫描的包 scanBasePackages{"待扫描包1","待扫描包2", . . . ," "} 指定需要扫描的类 scanBasePackageClasses{类1.class,类2.class,...} 方式二&#xff…

STM32F103C8----3-1 LED闪烁(跟着江科大学STM32)

一&#xff0c;电路图 接线图 面包板的的使用请参考&#xff1a;《面包板的使用_面包板的详细使用方法-CSDN博客》 二&#xff0c;目的/效果 2.1 推婉输出 外部供电&#xff08;熄的时间长&#xff09; 2.2 推婉输出 内部供电(亮的时间长) 三&#xff0c;创建Keil项目 详…

音乐项目总结(终)

总的来说写这个项目还是状态差了&#xff0c;前期中期写太慢&#xff0c;后期疯狂赶。 讲点对写这个项目能想起来解决的问题和写的的感触。 前期&#xff1a;当时觉得时间很充足&#xff0c;有布置算法题&#xff0c;我竟然还花三四天去学算法&#xff0c;&#xff0c;动态规划…

【网络安全】网络基础第一阶段——第三节:网络协议基础---- VLAN、Trunk与三层交换技术

目录 一、交换机 1.1 交换机定义 1.1.1 交换机 1.2 工作原理 1.2.1 数据帧的转发 1.2.2 交换机处理数据帧的三种行为 1.2.3 交换机通信 二、虚拟局域网&#xff08;VLAN&#xff09; 2.1 虚拟局域网简介 2.1.1 为什么需要VLAN 2.1.2 广播域的分割与VLAN的必要性 2.…

FPGA实现PCIE图片采集转HDMI输出,基于XDMA中断架构,提供3套工程源码和技术支持

目录 1、前言工程概述免责声明 2、相关方案推荐我已有的PCIE方案 3、PCIE基础知识扫描4、工程详细设计方案工程设计原理框图测试图片QT上位机XDMA配置及使用XDMA中断模块FDMA图像缓存Native视频时序生成RGB转HDMI输出模块Windows版本XDMA驱动安装Linux版本XDMA驱动安装工程源码…

Stable Diffusion绘画 | 来训练属于自己的模型:素材处理与打标篇

纵观整个模型训练流程&#xff0c;图片素材准备和打标环节占据的分量比重&#xff0c;绝对超过60%。 上一篇分享了图片素材准备&#xff0c;这一篇&#xff0c;开始对准备好的图片素材进行处理了。 素材处理 我已经收集了 霉霉 的25张图片&#xff1a; 但是&#xff0c;发现…

基于ESP8266—AT指令连接阿里云+MQTT透传数据(1)

在阿里云创建MQTT产品的过程涉及几个关键步骤,主要包括注册阿里云账号、实名认证、开通MQTT服务实例、创建产品与设备等。以下是详细的步骤说明: 一、准备工作 访问阿里云官网,点击注册按钮,填写相关信息(如账号、密码、手机号等)完成注册。注册完成后,需要对账号进行实…