((type *)0)->member的用法

news2024/11/23 16:49:20

 问题缘由,在学习 rt-thread 内核的时候遇到了这么一行代码:

to_thread = rt_list_entry(rt_thread_priority_table[0].next,
                          struct rt_thread,    
                          tlist);

而 rt_list_entry 的宏定义如下:

/* 已知一个结构体里面的成员的地址,反推出该结构体的首地址 */
#define rt_container_of(ptr, type, member) \ 
((type *)((char *)(ptr) - (unsigned long)(&((type *)0)->member)))

#define rt_list_entry(node, type, member) \ 
rt_container_of(node, type, member)

但是不理解 &((type *)0)->member 这段代码的意义,于是在网上搜集了一些资料后,总结如下:

该语句可以在不生成结构体实例的情况下计算结构体成员的偏移量,结构体变量的某成员的地址等于该结构体变量的基址加上结构体成员变量在结构体中的偏移量。而 (type*)0 就是假设地址 0 处存放的是一个 type 类型的结构体变量,这样的话这个结构体变量的基址就是 0,所以结构体成员 ((type *)0)->member 的地址的大小在数值上就等于该结构体成员在结构体中的偏移量

下面是一个简单的例子:

struct test {
    short a; // 偏移 0 个字节,占用 2 个字节
    short b; // 偏移 2 个字节,占用 2 个字节
    int c; // 偏移 4 个字节,占用 4 个字节
    int d; // 偏移 8 个字节,占用 4 个字节
};

int main(void) {

    printf("a 偏移 %d 个字节, 占用 %d 个字节\n", (unsigned long) &((struct test *)0)->a, (unsigned long) sizeof(((struct test *)0)->a));
    printf("b 偏移 %d 个字节, 占用 %d 个字节\n", (unsigned long) &((struct test *)0)->b, (unsigned long) sizeof(((struct test *)0)->b));
    printf("c 偏移 %d 个字节, 占用 %d 个字节\n", (unsigned long) &((struct test *)0)->c, (unsigned long) sizeof(((struct test *)0)->c));
    printf("d 偏移 %d 个字节, 占用 %d 个字节\n", (unsigned long) &((struct test *)0)->d, (unsigned long) sizeof(((struct test *)0)->d));
    return 0;
}

可以看到结果和我们预想的一样:

因为在 C 中,结构体内成员的偏移量是不会发生变化的,所以知道了一个结构体中的成员的地址和偏移量,我们就可以计算出结构体的地址,如下面这个例子:

struct test {
    short a; // 偏移 0 个字节,占用 2 个字节
    short b; // 偏移 2 个字节,占用 2 个字节
    int c; // 偏移 4 个字节,占用 4 个字节
    int d; // 偏移 8 个字节,占用 4 个字节
};

int main(void) {
    struct test t1;
    t1.a = 1;
    t1.b = 2;
    t1.c = 3;
    t1.d = 4;

    // 假设我们只知道 t1 结构体中 d 的地址
    int * d_ptr = &t1.d;
    // 那么我们可以通过如下方式获取到 t1 结构体的地址
    struct test *t2 = (struct test *)((char *)d_ptr - (unsigned long)(&((struct test *)0)->d));
    printf("a = %d, b = %d, c = %d, d = %d\n", t2->a, t2->b, t2->c, t2->d);
    return 0;
}

最终的结果如下:

在 rt-thread 中,链表结构体很简单,只有前向指针和后向指针两个参数:

如果有结构体需要使用链表结构的话,直接把上述结构体包含进来即可:

在遍历链表的时候,我们只需要用到 rt_list_t 结构体,找到对应的结构体后,我们再通过 rt_list_t 结构体及其偏移计算出 rt_thread 结构体的地址,是不是很灵活!

另外,在 linux 内核中,链表也是用这种方式实现的。 

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

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

相关文章

利用 Python 中的地理空间数据与 GeoPandas

推荐:使用 NSDT编辑器快速搭建3D应用场景 空间数据的真正潜力在于它能够连接数据点及其各自的位置,为高级分析创造无限的可能性。地理空间数据科学是数据科学中的一个新兴领域,旨在利用地理空间信息并通过空间算法和机器学习或深度学习等先进…

Android:使用命令行发现keytool不是内部命令解决办法

一、前言:最近在搞引入高德地图的SDK,发现需要给app签名打包。记录一下。 二、当我在命令行中输入keytool的时候说keytool不是内部命令 解决方案: 找到系统属性--------高级----------点击环境变量 双击点开 找到java\jre1.8.0_202\bin新建…

GLTF在线编辑器

GLTF在线编辑器提供了一个内置的模型查看器,可以加载和预览 glTF/glb 文件。用户可以在不用安装任何插件的情况下直接在浏览中快速查看和编辑器3D模型。 它的功能特点如下: 1、打开GLTF模型 用户可以在GLTF编辑器中拖入GLB/GLTF模型或者选择打开本地GL…

Bootstarp4 设计网页轮播组件

很多网站都有广告轮播功能,可使用bootstrap4中的carousel组件非常简单的实现。 目录 下载bootstrap4 轮播功能实现 简单实现轮播组件 增加标识图标 增加标题和说明 切换淡入淡出 设置数据间隔 总结 下载bootstrap4 下载 Bootstrap Bootstrap v4 中文文档 …

Android13 下拉菜单栏中添加快捷截图按钮

Android 13 原生系统下拉状态栏中是没有快捷截图按钮,现在需要添加快捷截图功能。 添加快捷截图功能后的效果图: 涉及修改的文件如下: modified: vendor/mediatek/proprietary/packages/apps/SystemUI/res/values/config.xml modified: vendor/mediatek/proprietary/…

软件测试的流程有哪些?

一、需求调研 1. 委托方提供资料 A. 填写测试委托申请表 B. 操作手册 C. 开发需求规格说明书 D. 开发合同及招标文件等 2. 双方技术沟通确定测试具体内容,如功能性测试、性能效率测试、信息安全性测试、兼容性测试、可靠性测试等。 3. 我方给出测试方案及报价…

SpringMVC学习|Servlet回顾、理解SpringMVC小demo、SpringMVC原理

Servlet回顾 创一个空的maven父工程,导入相关依赖,测试的、spring的、servlet的、jsp以及jstl标签的。 创建一个空的maven子项目,并添加web支持 为了保险起见,在子项目中添加servlet和jsp的依赖 编写一个Servlet类,实…

【Python】从入门到上头—常用内置模块基础应用(13)

文章目录 datetimebase64hashlibhmacurllibXMLHTMLParserrandom小结 datetime datetime是Python处理日期和时间的标准库。 获取当前日期时间 from datetime import datetime now datetime.now() # 获取当前datetime print(now) #2023-09-13 10:28:48.621343 print(type(no…

JSP ssm 网上求职管理系统myeclipse开发mysql数据库springMVC模式java编程计算机网页设计

一、源码特点 JSP ssm 网上求职管理系统是一套完善的web设计系统(系统采用SSM框架进行设计开发,springspringMVCmybatis),对理解JSP java编程开发语言有帮助,系统具有完整的源代码和数据库,系统主要采…

3900页手册415集视频426G资料迅为RK3568开发板

资料优势 专为3568编写|迅为原创|拒绝网络拼凑 20个手册2800页手册进行结构分层适用于学习与开发 为了方便大家清晰快速的学习,迅为iTOP-3568开发板手册资料全面升级,对手册内容进行了结构分层,共计20个文档,超2800页的资料专为…

sql注入之盲注总结

死在山野的风里,活在自由的梦里 sql注入之盲注总结 布尔 - 逻辑判断时间 - 延时判断报错 - 报错回显(强制性报错 )以DWVA靶场为例 盲注就是在注入的过程中,获取的数据不能显示到前端页面,此时,我们需要利用…

2023年武汉初级、中级、副高工程师职称评审条件是什么?甘建二告诉你

武汉中级职称评审通知已经出来了,武汉市中级、副高申报时间是8月31--9月18号,那么评这些职称需要满足什么条件呢?今天甘建二给大家分享一下评职称学历年限、专业等是怎么计算的,点赞收藏慢慢看。 一、武汉初级职称评审条件&#x…

【MySQL进阶】SQL性能分析

一、SQL性能分析 1.SQL执行频率 MySQL 客户端连接成功后,通过 show [session|global] status 命令可以提供服务器状态信 息。通过如下指令,可以查看当前数据库的 INSERT 、 UPDATE 、 DELETE 、 SELECT 的访问频次: -- session 是查看当…

jvm深入研究文档--整体概念

阿丹: 精通JVM对于一个java工程师非常重要,要是深入了解了jvm就可以有效的面对下面的问题 程序调优:JVM的配置和调优对于程序的运行有着至关重要的影响。不同的业务场景需要不同的JVM配置,比如设置不同的垃圾收集器、调整新生代和…

软考 - 计算机组成与体系笔记

数据的表示 进制转化 二进制转十进制(十进制以D表示) 从右往左,用二进制位上的数字乘以2的n次幂的和(n从0开始1累加) 十进制转二进制(二进制以B表示) 十进制数不断除以2直至到0,得…

电子智能充气泵pcba/芯片方案

智能充气泵是一种常见的气体压缩设备,它可以将空气或其他气体压缩成高压气体,用于充气、输送、压缩等工业和生活领域。智能充气泵的原理是利用机械或电动力量将气体压缩,使其体积减小,压力增大,从而达到充气的目的。电…

【echarts】如何将iconfont转换成echart所需的path路径 echarts折线图、柱状图如何设置自定义svg图标

步骤 下载iconfont图标到本地,用浏览器打开,右键查看源代码,或者用开发IDE软件打开,找到path d...,这个就是我们要传递给echart的icon的值。 代码示例: legend: {data: data?.map((item) > item.comp…

Fiddler抓http数据

目录 参考博客 一、Fiddler配置二、分析Http请求1. Http消息结构简介1.1 Request请求消息1.2 Response响应消息 2. 分析Get接口2.1 请求示例2.2 查看Get请求2.3 查看Get响应 3 分析Post接口 参考博客 一、Fiddler配置 首先需要对Fiddler抓取Https请求进行相关配置&#xff1a…

【九章斩题录】C/C++:判定字符是否唯一

精品题解 🔥 《九章斩题录》 👈 猛戳订阅 面试题 01.01. 判定字符是否唯一 ✅ 模板:C语言 class Solution { public:bool isUnique(string astr) {} }; 💭 思考:《程序员面试金典》里的题,这题和剑指Off…

typescript错误代码 error TS2451: 无法重新声明块范围变量“age”。ts(2451)

今天心血来潮写ts教程的时候发现一个问题,如下 短短的两行代码,竟然都报错,无法重新声明块范围变量age\。明明与其他文件没有相互依赖,却会提示 [ts] 无法重新声明块范围变量“age”。。且该文件目录夹下,也没有其他文件。为什么会有这个报错呢&#xff…