嵌入式C语言(三)

typeof()

使用typeof可以获取一个变量或表达式的类型。
typeof的参数有两种形式:表达式或类型。

int i;typeof(i) j = 20; --> int j = 20;typeof(int *) a; -->int *a;
int f();         -->typeof(f()) k;--? int k

我们可以看出通过typeof获取一个变量的类型int后,可以使用该类型再定义一个变量。

高级用法:

typeof (int *) y;-->int *y  y是一个指向int类型的指针typeof (int) *y;     执行int类型的指针变量ytypeof (*x) y;       y是一个指向x类型的指针  (这下是不是对前面两个的小小区别有所感悟)typeof (int) y[4];    y这个数组元素的类型是int  (换成x)就是x的类型   int y[4]   typeof (*x) y[4];   *x y[4]typeof (typeof (char *)[4]) y;//-->char *y[4]  字符指针数组  这个里面的数组元素都是 指针变量 typeof (char *)[4] --> char *[4]   type (char *)类型不就是括号里面的   然后 在加一层-->typeof(int x[4]) y; int y[4]对于以上其实观察的时候就一层层的剥开即可

typeof对于数组的操作还是挺花哨的,惊艳了。

container_of宏

Linux内核第一宏:container_of

听起来这么牛的宏,我们来一睹芳容一下

#define offsetof(TYPE, MEMBER) ((size_t) & ((TYPE *)0)->MEMBER)#define container_of(ptr,type,member)({ \const typeof( ((type *)0)->member) * __mptr = (ptr);\(type *)((char *)__mptr - offsetof(type,member));})

怎么样,内核里到处都是这。

宏中有宏。

三个参数:

  • type: 结构体的类型
  • member: 结构体成员
  • ptr: 结构体成员member地址

这个宏的作用:
通过结构体的某一成员的地址,来获取这个结构体的首地址。

这下再看是不是大概还是好点了。

我们来看个实用例子。

struct student
{int age;int num;int math;
};int main(void)
{struct student stu;struct student *p;p = container_of(&stu.num, struct student, num);return 0;
}

定义一个结构体类型student,
然后定义一个结构体变量stu,

知道了结构体成员变量stu.num的地址,

那么我们就可以通过container_of宏来获取结构体变量stu的首地址。

container_of这个玩意有什么用呢?

因为内核中有很多的结构体类型数据,为了抽象,会对结构体进行多次的封装,往往一个结构体里面又包含了多个结构体。无限套娃。不同的层次,不同的模块,使用的是对应的不同封装程度的结构体。

这个是不是有点让你联想到面向对象思想,没想到?没事我知道你认真专注于当下。

这样的优点我就不多说了。面向对象的优点。

在内核中,我们传递个函数的参数是某个结构体的成员,在这个函数中,还想用这个结构体的其他变量,这不是就需要container_of出场了。

找到了这个结构体的首地址–>

offsetof()函数主要用来计算member成员相对于结构体起始地址的偏移量。

现在我们来详细看看这个宏定义:

这里需要你先知道结构体怎么存储的。

结构体作为一个复合类型数据,它里面可以有多个成员。当我们定义一个结构体变量时,编译器要给这个变量在内存中分配存储空间根据每个成员的数据类型和字节对齐方式,编译器会按照结构体中各个成员的顺序,在内存中分配一片连续的空间来存储它们。

将数字0通过强制类型转换,转换为一个指向结构体类型为student的常量指针,然后分别打印这个常量指针指向的各成员地址。运行结果如下。

因为常量指针的值为0,即可以看作结构体首地址为0,所以结构体中每个成员变量的地址即该成员相对于结构体首地址的偏移。

知道了结构体的相对偏移地址,用结构体成员的地址减去相对偏移,就可以得到结构体的首地址。

从语法角度来看,container_of宏的实现由一个语句表达式构成。语句表达式的值即最后一个表达式的值。

取结构体某个成员member的地址,减去这个成员在结构体type中的偏移,运算结果就是结构体type的首地址。

因为语句表达式的值等于最后一个表达式的值,所以这个结果也是整个语句表达式的值,container_of最后会返回这个地址值给宏的调用者。

结构体的成员数据类型可以是任意数据类型,为了让这个宏兼容各种数据类型,我们定义了一个临时指针变量__mptr,该变量用来存储结构体成员MEMBER的地址,即存储宏中的参数ptr的值。如何获取ptr指针类型呢。


#define offsetof(TYPE, MEMBER) ((size_t) & ((TYPE *)0)->MEMBER)#define container_of(ptr,type,member)({ \const typeof( ((type *)0)->member) * __mptr = (ptr);\(type *)((char *)__mptr - offsetof(type,member));})三个参数:+ type: 结构体的类型+ member: 结构体成员+ ptr: 结构体成员member地址+ 指针变量__mptr:存储结构体成员MEMBER的地址-->ptr

我们知道,**宏的参数ptr代表的是一个结构体成员变量MEMBER的地址,**所以ptr的类型是一个指向MEMBER数据类型的指针,当我们使用临时指针变量__mptr来存储ptr的值时,必须确保__mptr的指针类型和ptr一样,是一个指向MEMBER类型的指针变量。

确保__mptr的指针类型和ptr一样

typeof(((type*)0)->member)表达式使用typeof关键字,用来获取结构体成员MEMBER的数据类型,然后使用该类型,通过typeof(((type*)0)->member)*__mptr这条程序语句,就可以定义一个指向该类型的指针变量了。

在语句表达式的最后,*因为返回的是结构体的首地址,所以整个地址还必须强制转换一下,转换为TYPE,即返回一个指向TYPE结构体类型的指针,*所以你会在最后一个表达式中看到一个强制类型转换(TYPE

这个文章也写的不错,讲的蛮清楚的。

在这里插入图片描述

一个parent代表结构体的类型,name代表结构体中的成员。

*((parent)0),**把数字0强制转换成parent 结构体指针类型。这样 ((parent )0) 这个整体就相当于一个指针指向了 0 这个地址,不管 0 这个地址是否合法,是否真的有这么一个结构体对象,它都会把以 0 地址为首的一片连续内存当成一个结构体对象操作。

((parent)0)->name,* *结构体指针((parent)0)取结构体对象中name成员。因为这只是对内存操作,**并没有写内存,虽然地址不合法也不会出现段错误。

*&((parent )0)->name对name成员取地址。

*offset = (uint32_t)&((parent )0)->name偏移量。 因为这个parent类型结构体对象是从0地址开始的,故而offset就是成员name的偏移量。

知道成员偏移量,就很容易求结构体对象本身的地址。成员的地址减去偏移量就是是结构体对象的首地址! – 本来的变量地址-便宜地址就是开始地方
((parent*)0)((uint32_t)node - (uint32_t)&((parent*)0)->name)结构体对象的首地址。

然后就可以开始访问变量了。->

到这里你应该懂了这个原理

[学习资料]:(https://www.freesion.com/article/57261214164/)
《嵌入式C语言自我修养:从芯片、编译器到操作系统》

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

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

相关文章

合并spark structured streaming处理流式数据产生的小文件

备注: By 远方时光原创,可转载,不能复制到其他平台 背景:做流批一体,湖仓一体的大数据架构,常见的做法就是 数据源->spark Streaming->ODS(数据湖)->spark streaming->…

Vue 实现页面导出A4标准大小的PDF文件,以及处理图片跨域不能正常展示的问题等

效果预览: 代码流程:首先在utils文件夹下创建htmlToPdf的js工具文件,然后在main.js中注册引用 htmlToPdf.js // 导出页面为PDF格式 import html2Canvas from html2canvas import JsPDF from jspdfexport default {install(Vue, options) {V…

hcia datacom课程学习(1):通信基础

1.总体框架 上图为发送方通过互联网传递信息给接收方的过程。 家用路由器会直接集成上图中的四层(vlan,DHCP,静态路由,NAT,PPPoE)。 2.网络性能指标 (1)带宽 单位时间内传输的数…

解析Hadoop三大核心组件:HDFS、MapReduce和YARN

目录 HadoopHadoop的优势 Hadoop的组成HDFS架构设计Yarn架构设计MapReduce架构设计 总结 在大数据时代,Hadoop作为一种开源的分布式计算框架,已经成为处理大规模数据的首选工具。它采用了分布式存储和计算的方式,能够高效地处理海量数据。Had…

pod调度策略 标签管理 资源配额与限额 全局资源配额与限额策略,

打分也是基于可调度节点进行打分资源情况. 指定多个节点,会进行覆盖其之前节点名称 --- kind: Pod apiVersion: v1 metadata:name: myhttp spec:nodeName: node-0001 # 基于节点名称进行调度containers:- name: apacheimage: myos:httpd 基于节点名称的调度策略 标签与调…

数据可视化--了解数据可视化和Excel数据可视化

目录 1.1科学可视化: 可视化是模式、关系、异常 1.2三基色原理: 三基色:红色、绿色和蓝色 1.3Excel数据可视化 1.3.1 excel数据分析-13个图表可视化技巧 1.3.2 excel数据分析-28个常用可视化图表(video) 1.3.3Excel可视化…

康复训练day2——2024牛客寒假集训营6

一道很好的构造题,受益匪浅。 链接:F-命运的抉择_2024牛客寒假算法基础集训营6 (nowcoder.com)​​​​​​ 题意: 题解 (并查集 思维): 首先将存在1的情况特判掉,我们的数组的元素都是> 2的…

2024-02-26(Spark,kafka)

1.Spark SQL是Spark的一个模块,用于处理海量结构化数据 限定:结构化数据处理 RDD的数据开发中,结构化,非结构化,半结构化数据都能处理。 2.为什么要学习SparkSQL SparkSQL是非常成熟的海量结构化数据处理框架。 学…

在having、select子句中使用子查询

目录 在having子句中使用子查询 统计出部门平均工资高于公司平均工资的部门编号、平均工资、部门人数 在select子句中使用子查询 查询每个员工的编号、姓名、职位、部门名称 Oracle从入门到总裁:https://blog.csdn.net/weixin_67859959/article/details/135209645 在havin…

销售线索获取 如何查找更多的销售线索平台

在进行销售工作时,寻找潜在客户和销售线索是非常重要的。只有及时地发现客户的需求和问题,才能更好地进行销售和提供服务。然而,在如今的市场环境中,客户的信息被广泛地分散在各个渠道和媒介上,如果仅靠人工搜索和整理…

如何优化Node.js应用的性能

随着Node.js在Web开发领域的广泛应用,越来越多的开发者开始关注如何优化Node.js应用的性能。优化Node.js应用的性能可以提升应用的响应速度,降低资源消耗,提升用户体验。在本文中,我们将探讨一些优化Node.js应用性能的方法和技巧。…

Nginx重写功能和反向代理

目录 一、重写功能rewrite 1. ngx_http_rewrite_module模块指令 1.1 if 指令 1.2 return 指令 1.3 set 指令 1.4 break 指令 2. rewrite 指令 3. 防盗链 3.1 实现盗链 3.2 实现防盗链 4. 实用网址 二、反向代理 1. 概述 2. 相关概念 3. 反向代理模块 4. 参数配置…

亿道丨三防平板丨如何从多方面选择合适的三防加固平板?

在如今这个信息爆炸的时代,移动设备已经成为我们生活和工作的必备工具。然而,在一些特殊的场合中,普通的平板电脑可能无法满足需求,比如工厂车间、野外作业、极端天气等环境下。此时,三防平板就成了不二之选。那么&…

SpringCloud-Docker安装与详解

Docker 是一款强大的容器化平台,通过其轻量级的容器技术,使应用程序的开发、部署和管理变得更加便捷和高效。本文将深入探讨 Docker 的安装过程,并详细解析其基本概念、组件及常用命令,以帮助读者充分理解和熟练使用 Docker。企业…

mac安装zookeeper

下载地址: http://archive.apache.org/dist/zookeeper/ 注意:由于Zookeeper从3.5.5版本开始,带有bin名称的包才是我们想要的下载可以直接使用的里面有编译后的二进制的包,而之前的普通的tar.gz的包里面是只是源码的包无法直接使…

Laravel04 eloquent

eloquent 1. eloquent2. 创建eloquent model 以及 取数据 1. eloquent 文档地址: https://learnku.com/docs/laravel/8.x/eloquent/9406 下面是我们,通过laravel的DB类从数据库中获取了post记录,那么有没有可能我们直接获取一个post对象&am…

Ps:索引颜色模式

Ps菜单:图像/模式/索引颜色 Image/Mode/Indexed Color 索引颜色 Indexed Color模式可生成最多 256 种颜色的 8 位图像文件。 这种颜色的限制使得索引颜色模式的图像文件相比于全彩图像(如 RGB 颜色模式下的图像)具有更小的文件大小&#xff0…

使用mumu模拟器开启调试安卓pwa程序环境,配置谷歌环境,并增加pwa安卓/苹果/PC电脑安装流程

环境配置 当你想开发pwa程序时,手上没有安卓手机怎么办?使用mumu安卓模拟器也是可以的,使用安卓模拟器后,可能自带的浏览器不支持pwa,还需要安装chrom浏览器,当你安装好chrom浏览器后,打开又提…

利用项目管理软件规划的成功之路

项目开发对于任何类型的项目都是一个有用的过程。软件开发项目、建筑项目、运输项目和变更管理项目都可以从这种方法提供的结构、指导和策略中获益。 项目开发涉及规划项目时间表、投资资源以及安排团队成员的时间。与项目管理一样,项目开发贯穿项目始终&#xff0…

用 React 实现搜索 GitHub 用户功能

用 React 实现搜索 GitHub 用户功能 在本篇博客中,我们将介绍如何在 React 应用中搜索 GitHub 用户并显示他们的信息。 创建 React 应用 首先,我们使用 Create React App 创建一个新的 React 应用。Create React App 是一个快速搭建 React 项目的工具…