Rust开发——数据对象的内存布局

枚举与Sized 数据

一般数据类型的布局是其大小(size)、对齐方式(align)及其字段的相对偏移量。

1. 枚举(Enum)的布局:

枚举类型在内存中的布局通常是由编译器来确定的。不同的编译器可能有不同的实现方式。一般来说,枚举的大小通常与其底层表示的整数类型相同,例如 enum 定义为 int 类型的大小。对于不同的枚举成员,编译器会分配不同的整数值。但是具体如何进行编码和布局是由编译器实现规定的。

在某些情况下,编译器可能会优化枚举类型的大小,特别是当枚举类型的取值在一个较小的范围内时,编译器可能会选择使用较小的整数类型来表示。

2. Sized 数据类型:

在 Rust 编程语言中,Sized 是一个特性(trait),用于表示大小在编译时是已知的类型。所有 Rust 中的类型默认都是 Sized 的,除非使用特殊的语法标记为 ?Sized,表示可能是动态大小的类型。

Rust 中的 size_ofalign_of 函数可以用于获取类型的大小和对齐方式:

  • std::mem::size_of::<T>() 返回类型 T 的大小(以字节为单位)。
  • std::mem::align_of::<T>() 返回类型 T 的对齐方式(以字节为单位)。

这些函数返回的结果是在编译时已知的常量值。

需要注意的是,对于不同的平台和编译器,类型的大小和对齐方式可能会有所不同。例如,在32位和64位系统上,同一类型的大小可能不同。

数字类型

在 Rust 中,数字类型的内存布局是按照它们的大小和规范来定义的。这些类型的内存布局通常受到硬件架构和编译器实现的影响。

在 Rust 中,常见的数字类型包括整数类型和浮点数类型。下面是一些常见的数字类型和它们的内存布局特征:

整数类型(Integer Types):

  1. 有符号整数

    • i8i16i32i64i128:根据其名称指示的位数存储有符号整数,例如,i32 是一个32位的有符号整数,i64 是一个64位的有符号整数。
    • 内存布局:按照补码表示存储。即最高位为符号位,其余位表示数值。
    • 在这里插入图片描述
  2. 无符号整数

    • u8u16u32u64u128:根据其名称指示的位数存储无符号整数,例如,u32 是一个32位的无符号整数,u64 是一个64位的无符号整数。
    • 内存布局:使用全部位来表示数值。
      在这里插入图片描述

浮点数类型(Floating-Point Types):

  1. 单精度浮点数

    • f32:32位的单精度浮点数。
    • 内存布局:采用IEEE 754标准,其中1位表示符号位,8位表示指数部分,23位表示尾数部分。
  2. 双精度浮点数

    • f64:64位的双精度浮点数。
    • 内存布局:同样采用IEEE 754标准,其中1位表示符号位,11位表示指数部分,52位表示尾数部分。
      在这里插入图片描述这些数字类型在内存中以固定大小进行存储,其具体的字节顺序和布局可能因编译器和硬件平台而异。一般来说,这些数字类型在内存中是连续存储的,并且其大小和布局遵循相应的标准,比如整数的补码表示和浮点数的IEEE 754标准。

usized 、isized

在 Rust 中,usizeisize 是特殊的整数类型,其大小取决于运行 Rust 代码的操作系统的位数。这两种类型的大小会根据不同系统的位数而变化。

  • usize:代表无符号整数,它的大小等于指针大小,即在 64 位系统上通常是 8 字节,在 32 位系统上通常是 4 字节。它用于表示内存中的索引、大小和指针偏移等。在 Rust 中,usize 类型通常用于索引集合或表示内存中对象的大小。

  • isize:代表有符号整数,同样,它的大小也等于指针大小。在 64 位系统上通常是 8 字节,在 32 位系统上通常是 4 字节。它可以用来表示可以为负数的索引或偏移量。

这两种类型在不同位数的系统上都是与机器字长相关的,这样可以更好地适应不同平台上的内存寻址和指针大小。它们的大小是 Rust 编译器根据目标平台自动确定的,并且在不同平台上可能会有所不同。

bool

bool 类型在 Rust 中用于表示布尔值,只能取 truefalse。它在内存中的长度和对齐长度都是 1 字节,在存储时占用一个字节的内存空间。

数组

在 Rust 中,数组是固定大小的相同类型元素的集合。Rust 中的数组具有连续的内存布局,并且元素是按照其定义顺序依次存储的。

内存布局:

  1. 连续存储:数组中的元素按照顺序在内存中连续存放。这意味着如果有一个包含多个元素的数组,它们会依次存储在内存中相邻的位置。

  2. 相同类型元素:数组中的所有元素必须是相同的类型。例如,一个 i32 类型的数组必须只包含 i32 类型的元素。

  3. 索引访问:通过索引可以访问数组的元素。Rust 中的数组索引从 0 开始,可以使用 [] 操作符来访问数组中特定索引的元素。

示例:

// 创建一个包含 4 个整数的数组
let my_array: [i32; 4] = [10, 20, 30, 40];

假设有一个名为 my_array 的数组,包含四个 i32 类型的整数。在内存中,这个数组的存储方式可能如下所示(这里只是示意,不考虑实际内存地址):

my_array: [10, 20, 30, 40]
Memory: | 10 | 20 | 30 | 40 |

这表示这个数组中的四个整数元素在内存中是连续存储的,每个整数占据相应的内存空间。数组的第一个元素 10 存储在第一个内存位置,第二个元素 20 存储在紧随其后的内存位置,以此类推。

通过索引可以访问数组中的元素,例如 my_array[0] 会返回第一个元素 10my_array[1] 返回第二个元素 20,以此类推。

str类型

1. char 类型:

  • char 表示一个 Unicode 字符,占据 32 位长度(4 字节)。
  • Unicode 标量值(Unicode Scalar Value)范围在 0x0000 - 0xD7FF0xE000 - 0x10FFFF

2. str 类型:

  • str 是一个不可变的 UTF-8 编码字符串片段(slice)。它是对 UTF-8 编码字节序列的引用。
  • Rust 的标准库假设 str 是有效的 UTF-8 编码,并且对其进行操作时会遵循这个假设。
  • str 的内存布局类似于 &[u8],但是假设其内容是有效的 UTF-8 编码。

3. slice

  • slice 是 Rust 中的动态大小类型(DST),表示一部分数据的视图。
  • &[T] 是一个“宽指针”,它存储了指向数据的地址和数据的长度。
  • slice 的内存布局与其指向的数据类型相同。

4. String 类型:

  • String 是 Rust 中的可变字符串类型,它拥有所有权,并且存储 UTF-8 编码的文本。
  • String 本质上是一个指向存储字符串数据的缓冲区的指针,它也是一个 Vec<u8> 的封装。
  • String 的内存布局包含了指向存储字符串数据的地址以及字符串的长度和容量信息。
    在 Rust 中,struct 是用于定义自定义数据类型的关键字,它允许组合多个不同类型的字段来创建一个新的复合类型。当涉及到 struct 的内存布局时,需要考虑其字段的排列和对齐方式。

struct

在 Rust 中,struct 的内存布局通常是紧凑且连续的,它的字段按照声明的顺序依次存储在内存中。然而,Rust 也会根据字段的类型和对齐需求进行内存对齐,以提高访问效率。

例如,考虑以下简单的结构体定义:

struct MyStruct {
    a: u8,
    b: u16,
    c: u32,
}

这个结构体 MyStruct 包含了一个 u8 类型的字段 a、一个 u16 类型的字段 b 和一个 u32 类型的字段 c。Rust 会根据这些字段的类型和对齐要求进行内存布局。

通常情况下,Rust 会按照字段的声明顺序进行内存排列,但也可能根据字段的大小和对齐要求进行调整,以保证每个字段能够按照其对齐规则正确地存储在内存中。

对齐规则是为了提高访问效率和减少内存访问的成本。例如,u16 类型通常需要在内存中对齐到 2 字节边界,u32 类型通常对齐到 4 字节边界,以此类推。因此,结构体的内存布局可能会在字段之间增加填充以满足对齐要求。

需要注意的是,Rust 并没有明确的规范指定结构体的内存布局,这是由编译器根据平台和优化策略进行管理的。因此,在特定情况下,结构体的内存布局可能会因编译器、目标架构或优化设置而有所不同。

tuple

元组(Tuple)是一种可以包含多个不同类型值的复合数据类型。元组的内存布局类似于结构体(struct),但有一些区别。

元组的内存布局:

  • 连续存储:元组中的元素按照其定义顺序在内存中连续存储。
  • 按照顺序排列:元组的元素按照声明的顺序存储在内存中,与结构体类似。
  • 无字段名:不同于结构体,元组的元素没有字段名,仅按照位置(索引)进行访问。

考虑一个简单的元组定义:

let my_tuple = (10, "hello", true);

这个元组 my_tuple 包含了一个 i32 类型的整数 10、一个 &str 类型的字符串 "hello" 和一个 bool 类型的布尔值 true

元组的内存布局是紧凑的,并且其中的元素按照它们在元组中的位置顺序存储。因为元组的元素没有字段名,访问元素时需要通过索引来获取,例如 my_tuple.0my_tuple.1my_tuple.2

和结构体不同,元组的内存布局没有对齐需求或填充。它们的存储是简单而紧凑的,元素之间紧密相邻。

元组的内存布局和访问方式是由 Rust 编译器管理的,而且 Rust 并未对元组的内存布局进行严格的规范。因此,具体的内存布局可能会因编译器、优化策略和目标平台等因素而有所不同。

closure

闭包(Closure)是一个可以捕获其环境中变量并且可以在稍后调用的匿名函数。闭包的内存布局在 Rust 中并没有直接暴露或详细定义,因为它们是由编译器在编译时根据捕获的环境和使用情况生成的。

尽管闭包的具体内存布局是由 Rust 编译器实现的细节,但闭包的内部实现与结构体有关,并且根据捕获的变量及其使用方式来进行编译。因此,理解闭包内存布局的最佳方式是从其行为和 Rust 的 trait 实现角度来理解闭包。

  1. 环境捕获:闭包可以捕获周围环境中的变量。这些捕获的变量在闭包被创建时会被包含在闭包的内部。

  2. Fn、FnMut 和 FnOnce:闭包的调用方式取决于其捕获的变量和如何使用这些变量。根据捕获变量的所有权或可变性,闭包可能实现了 FnOnceFnMutFn trait 中的其中之一。

  3. 背后的实现:闭包背后的具体实现通常涉及一个结构体,它包含了捕获的变量作为其字段,并且实现了对应的 trait。这个结构体的内存布局由编译器生成,通常会保证捕获变量的正确性和可访问性。

由于闭包的实现细节是由 Rust 编译器处理的,而不是直接暴露给开发者的,因此在大多数情况下,不需要过多关注闭包的具体内存布局。闭包的内存布局是由编译器根据其捕获的环境变量、使用方式和实现的 trait 来动态生成的,具体的实现细节可能会因编译器和闭包的用法而有所不同。

union

union 是一种特殊的数据结构,其所有字段共享相同的内存空间。每个 union 实例的大小由其最大字段的大小决定。

  1. 字段共享内存union 的所有字段共享同一块内存。因此,对一个字段的写入可能会覆盖其他字段的数据。

  2. 大小由最大字段决定union 的大小由其最大字段的大小决定。它的大小等于所有字段中最大字段的大小。

  3. 字段访问和数据有效性:在 union 中,程序员可以使用其中的某个字段来读取数据,但要确保读取的字段类型与实际存储的数据类型相匹配。否则,试图解释存储的数据为不匹配类型的行为将导致未定义的行为。

#[repr(C)]union 的声明:

#[repr(C)] 是一个属性,用于指示编译器按照 C 语言的布局规则来布局 union 或结构体的内存。它确保 union 的布局与 C 语言的标准兼容,有利于与 C 代码进行交互或者在 Rust 中实现特定的内存布局。

下面是一个示例 #[repr(C)]union 声明:

#[repr(C)]
union MyUnion {
    f1: u32,
    f2: f32,
}

在这个 union 中,f1 是一个 u32 类型的字段,f2 是一个 f32 类型的字段。由于这两个字段共享相同的内存空间,因此对一个字段的写入可能会影响另一个字段。
在使用 union 时,确保数据的有效性和正确的字段访问非常重要,否则可能导致不可预期的结果或未定义的行为。

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

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

相关文章

如何使用springboot服务端接口公网远程调试——实现HTTP服务监听

&#x1f308;个人主页&#xff1a;聆风吟 &#x1f525;系列专栏&#xff1a;网络奇遇记、数据结构、算法模板 &#x1f516;少年有梦不应止于心动&#xff0c;更要付诸行动。 文章目录 &#x1f4cb;前言一. 本地环境搭建1.1 环境参数1.2 搭建springboot服务项目 二. 内网穿透…

Java面试-微服务篇-SpringCloud

Java面试-微服务篇-SpringCloud SpringCloud 常见组件注册中心Eureka, Nacos负载均衡Ribbon服务雪崩, 熔断降级微服务的监控来源 SpringCloud 常见组件 通常情况下 Eureka: 注册中心Ribbon: 负载均衡Feign: 远程调用Hystrix: 服务熔断Zuul/Gateway: 网关 SpringCloudAlibaba…

C++程序中dump文件生成方法详解

最近项目中新作成了一个动态链接库&#xff0c;长时间运行后&#xff0c;偶尔会崩溃。根据log分析&#xff0c;被调用的动态库函数最外层catch到了这个异常&#xff0c;但是不能定位哪里出了问题。另外虽然上层exe是有dump文件输出处理的&#xff0c;但是在C中&#xff0c;如果…

Python requests请求响应以流stream的方式打印输出

如果你使用的请求库是requests&#xff0c;那么你必须了解的大模型里的请求怎么响应式的接收并打印出来的。 这里给大家写一下正式的书写方式: import requestsurl "http://localhost:8080/stream"payload {} headers {}response requests.request("GET&q…

创新洞察|展望2030 – 企业数字化转型的10大趋势(阿里研究院)

企业是否一定要 数字化创新 转型&#xff1f;究竟如何数字化转型&#xff1f;难点和坑又是什么&#xff1f;阿里研究院副院长针对未来十年中国的数字化转型提出十个方面需要关注的趋势&#xff1a;1.大国优势 2. 重构的消费者决策体系 3. 下一代数字原生企业 4. 所有企业都会成…

Endnote软件添加期刊引用格式

在下述网址中&#xff0c;找到你想要添加的期刊&#xff0c;下载引用格式文件&#xff08;后缀为.ens格式&#xff09; https://endnote.com/downloads/styles/?wpv_post_searchInformationfusion&wpv_aux_current_post_id12829&wpv_view_count12764-TCPID12829 下载…

ELK企业级日志分析平台——logstash

部署 新建一台虚拟机elk4部署logstash [rootelk4 ~]# yum install -y jdk-11.0.15_linux-x64_bin.rpm[rootelk4 ~]# yum install -y logstash-7.6.1.rpm 命令方式 [rootelk4 bin]# /usr/share/logstash/bin/logstash -e input { stdin { } } output { stdout {} } elasticsearc…

数仓成本下降近一半,StarRocks 存算分离助力云览科技业务出海

成都云览科技有限公司倾力打造了凤凰浏览器&#xff0c;专注于为海外用户提供服务&#xff0c;公司致力于构建一个全球性的数字内容连接入口&#xff0c;为用户带来更为优质、高效、个性化的浏览体验。 作为数据驱动的高科技公司&#xff0c;从数据中挖掘价值一直是公司核心任务…

【C++】——标准模板库STL作业(其一)

&#x1f383;个人专栏&#xff1a; &#x1f42c; 算法设计与分析&#xff1a;算法设计与分析_IT闫的博客-CSDN博客 &#x1f433;Java基础&#xff1a;Java基础_IT闫的博客-CSDN博客 &#x1f40b;c语言&#xff1a;c语言_IT闫的博客-CSDN博客 &#x1f41f;MySQL&#xff1a…

Python-Django的“日志功能-日志模块(logging模块)-日志输出”的功能详解

01-综述 可以使用Python内置的logging模块来实现Django项目的日志记录。 所以与其说这篇文章在讲Django的“日志功能-日志模块-日志输出”&#xff0c;不如说是在讲Pthon的“日志功能-日志模块-日志输出”&#xff0c;即Python的logging模块。 下面用一个实例来进行讲解。 …

纯干货之阿里云云计算认证,赶紧收藏!

一、阿里云&云计算认证&#xff0c;引领未来 想必大家对阿里这个企业都很熟悉&#xff0c;我们平时常用的支付宝、淘宝、钉钉、飞猪等等都是阿里的产业&#xff0c;用在我们生活的各个方面。 但大家可能不知道的是&#xff0c;阿里云的云计算技术也是领先全国甚至全球的&…

机器学习实战-第4章 基于概率论的分类方法: 朴素贝叶斯

朴素贝叶斯 概述 贝叶斯分类是一类分类算法的总称,这类算法均以贝叶斯定理为基础,故统称为贝叶斯分类。本章首先介绍贝叶斯分类算法的基础——贝叶斯定理。最后,我们通过实例来讨论贝叶斯分类的中最简单的一种: 朴素贝叶斯分类。 贝叶斯理论 & 条件概率 贝叶斯理论 …

CAN基础知识

CAN 简介 CAN 是 Controller Area Network 的缩写&#xff08;以下称为 CAN&#xff09;&#xff0c;是 ISO 国际标准化的串行通信 协议。在当前的汽车产业中&#xff0c;出于对安全性、舒适性、方便性、低公害、低成本的要求&#xff0c;各种 各样的电子控制系统被开发了出来…

保姆级 Keras 实现 YOLO v3 一

保姆级 Keras 实现 YOLO v3 一 一. YOLO v3 总览二. 特征提取网络特征提取网络代码实现 三. 特征融合特征融合代码实现 四. 网络输出模型输出代码实现 五. 网络模型代码实现六. 代码下载 如果要给 YOLO 目标检测算法一个评价的话, 就是快和准, 现在已经到了 v8, 但是我为什么还…

【考研数据结构代码题7】求一元多项式之和

题目&#xff1a;编写一个算法&#xff0c;求一元多项式之和 考纲&#xff1a;一元多项式的表示与相加 题型&#xff1a;代码填空或算法设计 难度&#xff1a;★★★ 参考代码 typedef struct node{float coef;//系数int exp;//次数struct node *next; }polynode; polynode *…

Volcano3D绘制3D火山图

一边学习&#xff0c;一边总结&#xff0c;一边分享&#xff01; 本期教程内容 **注&#xff1a;**本教程详细内容 Volcano3D绘制3D火山图 一、前言 火山图是做差异分析中最常用到的图形&#xff0c;在前面的推文中&#xff0c;我们也推出了好几期火山图的绘制教程&#xff0…

红队攻防实战之内网穿透隐秘隧道搭建

别低头&#xff0c;皇冠会掉&#xff1b;别流泪&#xff0c;贱人会笑。 本文首发于先知社区&#xff0c;原创作者即是本人 0x00 前言 构建内网隐蔽通道&#xff0c;从而突破各种安全策略限制&#xff0c;实现对目标服务器的完美控制。 当我们从外网成功获得攻击点的时候&…

【LeetCode】挑战100天 Day13(热题+面试经典150题)

【LeetCode】挑战100天 Day13&#xff08;热题面试经典150题&#xff09; 一、LeetCode介绍二、LeetCode 热题 HOT 100-152.1 题目2.2 题解 三、面试经典 150 题-153.1 题目3.2 题解 一、LeetCode介绍 LeetCode是一个在线编程网站&#xff0c;提供各种算法和数据结构的题目&…

ComfyUI搭建使用教程

ComfyUI 是一个基于节点流程式的stable diffusion AI 绘图工具WebUI&#xff0c; 你可以把它想象成集成了stable diffusion功能的substance designer&#xff0c; 通过将stable diffusion的流程拆分成节点&#xff0c;实现了更加精准的工作流定制和完善的可复现性。但节点式的工…

STM32F103C8T6第6天:adc、iic、spi、温湿度dht11在lcd1602显示

1. ADC介绍 ADC是什么&#xff1f; Analog-to-Digital Converter&#xff0c;指模拟/数字转换器 ADC的性能指标 量程&#xff1a;能测量的电压范围分辨率&#xff1a;ADC能辨别的最小模拟量&#xff0c;通常以输出二进制数的位数表示&#xff0c;比如&#xff1a;8、10、1…