嵌入式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类型的指针变量y

typeof (*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语言自我修养:从芯片、编译器到操作系统》

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

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

相关文章

【iOS ARKit】ARWorldMap

ARWorldMap 用于存储 ARSession 检测扫描到的空间信息数据,包括地标(Landmark)、特征点(Feature Point)、平面(Plane)等,以及使用者的操作信息,如使用者添加的 ARAnchor …

LabVIEW高精度闭式微小型循环泵性能测试

LabVIEW高精度闭式微小型循环泵性能测试 开发了一套基于LabVIEW的高精度闭式微小型循环泵性能测试系统,旨在通过先进的测试技术和虚拟仪器技术,对微小型循环泵的性能进行精确测量和分析,从而优化泵的设计和性能,提高其在航空、机…

助力智能化农田作物除草,基于DETR(DEtection TRansformer)模型开发构建农田作物场景下玉米苗、杂草检测识别分析系统

在我们前面的系列博文中,关于田间作物场景下的作物、杂草检测已经有过相关的开发实践了,结合智能化的设备可以实现只能除草等操作,玉米作物场景下的杂草检测我们则少有涉及,这里本文的主要目的就是想要基于DETR模型来开发构建玉米…

Vue-3

自定义指令 全局注册指令 文件路径:src/main.js import Vue from vue import App from ./App.vue Vue.config.productionTip false// 全局注册指令 Vue.directive(myFocus, {// inserted 会在指令所在的元素,被插入到页面中时触发inserted(el) {el.f…

Ps:颜色模式

Photoshop 中的颜色模式 Color Mode定义了图像中使用的颜色系统,这些模式影响图像的颜色表现、文件大小以及可适用的场景。 Ps菜单:图像/模式 Mode 在不同的颜色模式下,会基于不同的通道并使用不同的方式来混合颜色。 RGB、CMYK、Lab 颜色模…

Centos 7.5 上nginx设置开机自启动

nginx的安装目录 : /usr/local/nginx 一、没有设置开机自启动前,需要执行/usr/local/nginx/sbin/nginx 启动 二、接下来,我们设置开机自启动,就不用手动启动nginx了 1、cd /usr/lib/systemd/system/ 2、vi nginx.service [un…

SpringMVC(1)

目录 SpringMVC简介入门案例启动服务器初始化过程单次请求过程bean加载控制 PostMan请求与响应设置请求映射路径请求参数五种类型参数传递JSON数据日期类型参数传递响应 RestRest 简介RESTful快速开发 SpringMVC是隶属于Spring框架的一部分,主要是用来进行Web开发&a…

vue + koa + 阿里云部署 + 宝塔:宝塔前后端部署

接上篇,我们已经完成了宝塔的基本配置,下面我们来看如何在宝塔中部署前后端 一、上传前后端代码文件 在www > wwwroot目录下创建了一个demo文件,用来存放前后端代码 进入demo中,点击上传 这里前端我用的打完包的 dist文件&am…

【踩坑】修复xrdp无法关闭Authentication Required验证窗口

转载请注明出处:小锋学长生活大爆炸[xfxuezhang.cn] 问题如下,时不时出现,有时还怎么都关不掉,很烦: 解决方法一:命令行输入 dbus-send --typemethod_call --destorg.gnome.Shell /org/gnome/Shell org.gn…

excel 实现分组排序功能

我们经常会遇到按照分组进行排序,在excel如何实现呢? 如下列的数据,需要按照分组,将得分从高到底排名 我们可以使用如下的公式操作即可实现 SUMPRODUCT((A$2:A$15A2)*(C$2:C$15>C2))1

【漏洞复现】大华智慧园区综合管理平台信息泄露漏洞

Nx01 产品简介 大华智慧园区综合管理平台是一款综合管理平台,具备园区运营、资源调配和智能服务等功能。该平台旨在协助优化园区资源分配,满足多元化的管理需求,同时通过提供智能服务,增强使用体验。 Nx02 漏洞描述 大华智慧园区…

腾轩科技传媒分享创建企业百度百科词条前期要点

百度百科是企业的重要名片之一,一个优秀的百度百科词条可以为企业增添无限魅力和影响力,如何创建一篇引人注目的企业百度百科词条呢?接下来,希望大家和腾轩科技传媒一起来学习如何创建企业百度百科词条吧! 1、精心准备…

记录一下 Unity团结引擎开发OpenHarmony Next 应用 环境搭建流程

原视频链接 记录环境搭建过程~,本文是图文版本 一、打开团结引擎官网下载对应的 团结引擎版本 官网地址:https://unity.cn/tuanjie/releases 根据各自的开发环境下载对应的软件版本,我是 windwos 环境,我就下载 windows 环境 …

使用R语言进行多元线性回归分析-多重共线的诊断

一、数据集 序号X1x2x3x4Y序号X1x2x3X4Y12666078.57831224472.51229155274.31954182293.12356850104.3111047426115.92143184787.6111140233483.8155263395.971266912113.311655922109.2111368812109.410771176102.73       1、从中选取主要变量,建立与因变…

理想滤波器、巴特沃斯滤波器、高斯滤波器实现(包含低通与高通,代码实现与分析)

本篇博客聚焦理想滤波器、巴特沃斯滤波器、高斯滤波器进行原理剖析、代码实现和结果总结,代码含有详细注释,希望帮助大家理解。 以下将从理想低通滤波器、理想高通滤波器、巴特沃斯低通滤波器、巴特沃斯高通滤波器、高斯低通滤波器、高斯高通滤波器六个…

B站UP视频播放数据分析之然冉创业说

【背景介绍】 几年前做过类似的分析,但是B站数据加密了,刚好最近在用selenium,就顺手用它爬一下数据。 df pd.read_excel("然冉创业说_13.2万_output.xlsx") df.head() 以上数据在视频播放页面就可以获取到。 【数据分析】 从数…

进程间通信学习笔记(有名管道和无名管道)

进程间通信方式: 无名管道(pipe)有名管道(fifo)信号(signal)共享内存(mmap)套接字(socket) 无名管道: 在内核里面开辟一片内存,进程1和进程2都可以通过这片内存进行通信 无名管道特点: 只能用于具有亲缘关系的进程之间的通信&am…

SocketError | Socket错误码一览表(每一种错误码的故障排查建议)

Socket错误码一览表 文章目录 Socket错误码一览表前言错误码表 前言 在软件开发和网络通信编程中,SocketError算是一个绕不开的坎。它可能因为各种原因而来,比如网络问题、用户搞错了、应用程序出错等等。本文整理一张SocketError排查建议表格就是为了帮…

Python打发无聊时光:8.用kivy库实现滑动控温的空调界面

第一步:装kivy库 在终端输入: pip install kivy 第二步:复制代码 from kivy.app import App from kivy.uix.boxlayout import BoxLayout from kivy.uix.slider import Slider from kivy.uix.label import Label from kivy.uix.togglebutton import T…

多模态表征—CLIP及中文版Chinese-CLIP:理论讲解、代码微调与论文阅读

我之前一直在使用CLIP/Chinese-CLIP,但并未进行过系统的疏导。这次正好可以详细解释一下。相比于CLIP模型,Chinese-CLIP更适合我们的应用和微调,因为原始的CLIP模型只支持英文,对于我们的中文应用来说不够友好。Chinese-CLIP很好地…