Rust: 从内存地址信息看内存布局

内存布局其实有几个:address(地址)、size(大小)、alignment(对齐位数,2 的自然数次幂,2,4,8…)。
今天主要从address来看内存的布局。

说明:下面以Struct,Enum以默认对齐的情况下(不包括C对齐即#[repr( C )]、 紧凑对齐 #[repr(packed)]、自定义对齐 #[repr(align(n))] 等情况)进行分析。

一、代码


#[derive(Default)]
struct BaseStruct {field1: u8,field2: u8,field3: i32,field4: u8,field5: u8,field6: u8,field7: u8,
}struct MyStruct{field1:u8,field2:BaseStruct,field3:MyEnum,field4:i64,
}
impl MyStruct{fn default() -> Self {MyStruct {field1: 1,field2: BaseStruct::default(),field3: MyEnum::Variant1(1),field4: 2,}}
}
// Enum的内存大小:最大字段大小size+8个字节,下面最大为i64:8个字节 +8 =16enum MyEnum {Variant1(i64),Variant2(i8),Variant3(i64),Variant4(i64),
}fn main() {println!("Size of u64                   : {}", std::mem::size_of::<u64>());println!("Size of i64                   : {}", std::mem::size_of::<i64>());println!("Size of MyEnum                : {}", std::mem::size_of::<MyEnum>());println!("Align of MyEnum               : {}", std::mem::align_of::<MyEnum>());println!("Size of BaseStruct            : {}", std::mem::size_of::<BaseStruct>());println!("Align of BaseStruct           : {}", std::mem::align_of::<BaseStruct>());println!("Size of MyStruct              : {}", std::mem::size_of::<MyStruct>());println!("Align of MyStruct             : {}", std::mem::align_of::<MyStruct>());println!("-------------BaseStruct-------------");let base_struct = BaseStruct::default();println!("address of base_struct        : {}",get_ptr_address(&base_struct));println!("address of base_struct_field_1: {}",get_ptr_address(&base_struct.field1));println!("address of base_struct_field_2: {}",get_ptr_address(&base_struct.field2));println!("address of base_struct_field_3: {}",get_ptr_address(&base_struct.field3));println!("address of base_struct_field_4: {}",get_ptr_address(&base_struct.field4));println!("address of base_struct_field_5: {}",get_ptr_address(&base_struct.field5));println!("address of base_struct_field_6: {}",get_ptr_address(&base_struct.field6));println!("address of base_struct_field_7: {}",get_ptr_address(&base_struct.field7));println!("-------------MyStruct-------------");let my_struct = MyStruct::default();println!("address of my_struct          : {}",get_ptr_address(&my_struct));println!("address of my_struct_field_1  : {}",get_ptr_address(&my_struct.field1));println!("address of my_struct_field_2  : {}",get_ptr_address(&my_struct.field2));println!("address of my_struct_field_3  : {}",get_ptr_address(&my_struct.field3));println!("address of my_struct_field_4  : {}",get_ptr_address(&my_struct.field4));println!("-----------get_ptr_address_2-----------");println!("address_2 of my_struct          : {}",get_ptr_address_2(&my_struct));println!("address_2 of my_struct_field_1  : {}",get_ptr_address_2(&my_struct.field1));println!("address_2 of my_struct_field_2  : {}",get_ptr_address_2(&my_struct.field2));println!("address_2 of my_struct_field_3  : {}",get_ptr_address_2(&my_struct.field3));println!("address_2 of my_struct_field_4  : {}",get_ptr_address_2(&my_struct.field4));}fn get_ptr_address<T>(data: &T)->usize{data as *const _ as usize //会先创建一个引用
}
// 为什么不能用这个?
fn get_ptr_address_2<T>(data: &T)->usize{std::ptr::addr_of!(data) as usize //不需要创建一个引用
}

二、输出

Size of u64                   : 8
Size of i64                   : 8
Size of MyEnum                : 16
Align of MyEnum               : 8
Size of BaseStruct            : 12
Align of BaseStruct           : 4
Size of MyStruct              : 40
Align of MyStruct             : 8
-------------BaseStruct-------------
address of base_struct        : 490357126640
address of base_struct_field_1: 490357126644
address of base_struct_field_2: 490357126645
address of base_struct_field_3: 490357126640
address of base_struct_field_4: 490357126646
address of base_struct_field_5: 490357126647
address of base_struct_field_6: 490357126648
address of base_struct_field_7: 490357126649
-------------MyStruct-------------
address of my_struct          : 490357126600
address of my_struct_field_1  : 490357126636
address of my_struct_field_2  : 490357126624  // basestruct
address of my_struct_field_3  : 490357126600  // myenum
address of my_struct_field_4  : 490357126616
-----------get_ptr_address_2-----------
address_2 of my_struct          : 490357126528
address_2 of my_struct_field_1  : 490357126528
address_2 of my_struct_field_2  : 490357126528
address_2 of my_struct_field_3  : 490357126528
address_2 of my_struct_field_4  : 490357126528

三、问题

可以对照一下address的顺序,来看一下各个field以及对应Struct、Enum的大小。

1、为什么BaseStruct的大小并不是field的起始地址到field的最后地址?

BaseStruct的真实size是12个字节。但起始address起始间隔目前只看到9。为什么?

从地址信息可以看出,
(1)field3(i32),重新布局后,已经放在前面,并不是从field1开始。地址是从490357126640->490357126644:占了4个字节。
(2)接下来是field1,field2,field4,field5,依次占了1个字节。
(3)再接下来是field6,field7,各占1个字节。
因为在进行内存布局优化时,已经按4+4+4的格局进行优化,这里面4就是BaseStruct alignment。

在最后4个字节中,field6,field7已经用掉了2个,还有2个空的(alignment padding)。因此是4+4+2+2=12。

其实:BaseStruct如果没有field7,或者说,再加一个field8(u8),其size均是12个字节!

这个在MyStruct中各field的地址信息,可以更真实显示BaseStruct(field2)的大小(即上下间隔)。即490357126624 ->490357126636(注:每次运行不一样)间隔为12。

当然,MyStruct也是一样,不能把field首地址和未地址相减得到其占用大小(计算得到36个字节,并不是40个字节!)。它的alignment是8。

因此需要注意的是:最后一个,是有alignment的。有些是只有部分占用。因此不能简单通过首地址和末地址相减来获得这个大小。

2、为什么add_of!在这儿不能用?

fn get_ptr_address_2<T>(data: &T)->usize{std::ptr::addr_of!(data) as usize //不需要创建一个引用
}

add_of宏生成了同样的地址。

只需要进行下面的修改就可以了。add_of宏不需要是对象的引用,直接用对象本身,不用担心会消费掉所有权。

println!("address_2 of my_struct          : {}",std::ptr::addr_of!(my_struct) as usize);
println!("address_2 of my_struct_field_1  : {}",std::ptr::addr_of!(my_struct.field1) as usize);
println!("address_2 of my_struct_field_2  : {}",std::ptr::addr_of!(my_struct.field2) as usize);
println!("address_2 of my_struct_field_3  : {}",std::ptr::addr_of!(my_struct.field3) as usize);
println!("address_2 of my_struct_field_4  : {}",std::ptr::addr_of!(my_struct.field4) as usize);

上面的修改和

fn get_ptr_address<T>(data: &T)->usize{data as *const _ as usize //会先创建一个引用
}

效果一样。

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

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

相关文章

每日一题算法——两个数组的交集

两个数组的交集 力扣题目链接 我的解法&#xff1a;利用数组下标。 缺点&#xff1a;当取值范围很大时&#xff0c;浪费空间。 class Solution { public:vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {int count1[1001]{0…

c++ 互斥锁

为练习c 线程同步&#xff0c;做了LeeCode 1114题. 按序打印&#xff1a; 给你一个类&#xff1a; public class Foo {public void first() { print("first"); }public void second() { print("second"); }public void third() { print("third"…

山东大学软件学院创新项目实训开发日志(20)之中医知识问答自动生成对话标题bug修改

在原代码中存在一个bug&#xff1a;当前对话的标题不是现有对话的用户的第一段的前几个字&#xff0c;而是历史对话的第一段的前几个字。 这是生成标题的逻辑出了错误&#xff1a; 当改成size()-1即可

WSL2-Ubuntu22.04下拉取Docker MongoDB镜像并启动

若未安装docker可参考此教程&#xff1a;可以直接在wsl上安装docker吗&#xff0c;而不是安装docker desktop&#xff1f;-CSDN博客 1. 拉取镜像 docker pull mongo:latest 2.打开网络加速&#xff0c;再次拉取镜像 3.创建docker-compose.yml 进入vim编辑器后输入i进行编辑&a…

中通 Redis 集群从 VM 迁移至 PVE:技术差异、PVE 优劣势及应用场景深度解析

在数字化转型浪潮下&#xff0c;企业对服务器资源的高效利用与成本控制愈发重视。近期&#xff0c;中通快递将服务器上的 Redis 集群服务从 VM&#xff08;VMware 虚拟化技术&#xff09;迁移至 PVE&#xff08;Proxmox VE&#xff09;&#xff0c;这一技术举措引发了行业广泛关…

Prometheus+Grafana实时监控系统各项指标

一、监控架构设计 核心组件与数据流 Prometheus&#xff1a;时序数据采集、存储与告警规则管理Node Exporter&#xff1a;采集主机指标&#xff08;CPU、内存、磁盘、网络等&#xff09;数据库Exporter&#xff1a;如 mysqld_exporter、postgres_exporterGrafana&#xff1a;…

[密码学基础]GMT 0029-2014签名验签服务器技术规范深度解析

GMT 0029-2014签名验签服务器技术规范深度解析 引言 在数字化转型和网络安全需求激增的背景下&#xff0c;密码技术成为保障数据完整性与身份认证的核心手段。中国密码管理局发布的GMT 0029-2014《签名验签服务器技术规范》&#xff0c;为签名验签服务器的设计、开发与部署提…

多路转接select服务器

目录 select函数原型 select服务器 select的缺点 前面介绍过多路转接就是能同时等待多个文件描述符&#xff0c;这篇文章介绍一下多路转接方案中的select的使用 select函数原型 #include <sys/select.h> int select(int nfds, fd_set *readfds, fd_set *writefds, f…

QT6 源(45):分隔条 QSplitter 允许程序的用户修改布局,程序员使用 IDE时,就是分隔条的用户,以及其 QSplitter 源代码

&#xff08;1&#xff09; &#xff08;2&#xff09;本类的继承关系如下&#xff0c;所以说分隔条属于容器&#xff1a; &#xff08;3&#xff09;本类的属性&#xff1a; &#xff08;4&#xff09; 这是一份 QSplitter 的举例代码&#xff0c;注意其构造函数时候的传参&am…

VSCode PIO使用Jlink SWD烧录Stm32

一、背景 PIO的编译速度比Arduino快很多&#xff0c;同样支持Arduino的语法。VScode的自动补全和插件也能够帮助快速开发目前使用JLINK SWD的方式连接STM32 二、配置 在ini配置文件中&#xff0c;添加如下内容 [env:genericSTM32F103C8] platform ststm32 board genericS…

JavaScript 渲染内容爬取:Puppeteer 入门

在现代网络应用中&#xff0c;许多网页内容是通过 JavaScript 渲染生成的&#xff0c;传统的爬虫工具往往难以获取这些动态内容。Puppeteer 作为一种强大的浏览器自动化工具&#xff0c;为这一问题提供了优雅的解决方案。本文将带你入门 Puppeteer&#xff0c;介绍如何安装、启…

卷积神经网络:视觉炼金术士的数学魔法

引言&#xff1a;当数学遇见视觉炼金术 在人工智能的奇幻世界里&#xff0c;卷积神经网络&#xff08;CNN&#xff09;犹如掌握视觉奥秘的炼金术士&#xff0c;将原始像素的"铅块"淬炼成认知的"黄金"。这种融合数学严谨性与生物灵感的算法架构&#xff0c…

Android Cordova 开发 - Cordova 快速入门(Cordova 环境配置、Cordova 第一个应用程序)

一、Cordova 1、Cordova 概述 Cordova 是使用 HTML&#xff0c;CSS 和 JavaScript 构建混合移动应用程序的平台 2、Cordova 特征 &#xff08;1&#xff09;命令行界面&#xff08;Cordova CLI&#xff09; 这是可用于启动项目&#xff0c;构建不同平台的进程&#xff0c;…

ubuntu18.04启动不了修复

参考: 虚拟机里的Ubuntu18.4启动时进入到grub rescue救援模式&#xff08;无法正常进入到系统&#xff09;&#xff0c;ls查看后只有一个硬盘和分区&#xff0c;且无法找到/boot/grub文件【已解决】_ubuntu grub rescue-CSDN博客 本人fdisk错误使用,导致了grub启动不了 第一步…

SpringBoot3设置maven package直接打包成二进制可执行文件

注意事项 SpringBoot普通native打包顺序clean compile spring-boot:process-aot native:compile 使用以下配置只会的打包顺序clean package&#xff08;注意&#xff1a;使用此配置以后打包会有编译后的class文件、jar包、original源文件、二进制可执行文件【Linux是无后缀的包…

【华为】防火墙双击热备-之-主备模式-单外网线路

FW1和FW2的业务接口都工作在三层&#xff0c;上行连接二层交换机。上行交换机连接运营商的接入点&#xff0c;运营商为企业分配的IP地址为100.100.100.2。现在希望FW1和FW2以主备备份方式工作。正常情况下&#xff0c;流量通过FW1转发&#xff1b;当FW1出现故障时&#xff0c;流…

MYSQL之表的操作

1. 创建表 语法: CREATE TABLE table_name ( field1 datatype, field2 datatype, field3 datatype ) character set 字符集 collate 校验规则 engine 存储引擎; field 表示列名, datatype 表示列的类型character set 字符集, 如果没有指定字符集, 则以所在数据库的字符集为…

RAG进阶:Chroma开源的AI原生向量数据库

一、Chroma 核心概念与优势 1. 什么是 Chroma&#xff1f; Chroma 是一款开源的向量数据库&#xff0c;专为高效存储和检索高维向量数据设计。其核心能力在于语义相似性搜索&#xff0c;支持文本、图像等嵌入向量的快速匹配&#xff0c;广泛应用于大模型上下文增强&#xff0…

店匠科技摘得 36 氪“2025 AI Partner 创新大奖”

全场景 AI 方案驱动跨境电商数智化跃迁 4 月 18 日,36 氪 2025 AI Partner 大会于上海盛大开幕。大会紧扣“Super App 来了”主题,全力探寻 AI 时代的全新变量,探索 AI 领域下一个超级应用的无限可能性。在此次大会上,跨境电商独立站 SaaS 平台店匠科技(Shoplazza)凭借“店匠跨…

SQL技术终极指南:从内核原理到超大规模应用

一、DDL核心应用场景与最佳实践 1.1 表结构设计场景矩阵 业务场景核心语法要素典型实现案例电商用户画像JSON字段虚拟列索引CREATE TABLE users (id INT, profile JSON, AS (profile->>$.age) VIRTUAL, INDEX idx_age((profile->>$.age)))物联网时序数据分区表压…