使用lldb看看Rust的HashMap

news2025/5/12 20:17:33

目录

前言

正文

读取桶的状态

获取键值对

键值对的指针地址

此时,读取数据

读取索引4的键值对

多添加几个键值对

 使用i32作为键,&str作为值

使用i32作为键,String作为值


前言

前面使用ldb看了看不同的类型,这篇再使用lldb就来看看HashMap

使用lldb查看Rust不同类型的结构-CSDN博客https://blog.csdn.net/qq_63401240/article/details/147839957?spm=1001.2014.3001.5501

正文

use std::collections::HashMap;

fn main() {
    let mut map = HashMap::new();
    // 插入键值对
    map.insert("go", "tt");
    map.insert("66", "123");
    map.insert("ok", "no");


    println!("最终的 HashMap: {:?}", map);
}

这段简单地的代码,笔者在println哪里打个断点,然后用lldb查看

首先

p map

效果和expr 的等效的  

(lldb) p map
(std::collections::hash::map::HashMap<ref$<str$>,ref$<str$>,std::hash::random::RandomState>) map = {
  base = {
    hash_builder = (k0 = 4722932796647620636, k1 = 13347288390664849048)
    table = {
      table = {
        bucket_mask = 3
        ctrl = {
          pointer = 0x000001bc42010e00
        }
        growth_left = 0
        items = 3
      }
      alloc = {}
      marker = {}
    }
  }
}

 这个就看出HashMap在Rust的结构吧,笔者也不知道用什么词表示,就用结构这个词表示。

这里最关键的东西,显然易见是pointer,里面是一个地址。

结合HashMap有关的知识
【数据结构】哈希表-CSDN博客https://blog.csdn.net/jmlangel_/article/details/147423680

 7. HashMap的底层数据结构 - 知乎https://zhuanlan.zhihu.com/p/636054806

关注

bucket_mask = 3
ctrl = {
          pointer = 0x000001bc42010e00
    }
        growth_left = 0
        items = 3

这几个的意思

bucket_mask = 3:哈希表有 3+1 个桶。
ctrl.pointer = 0x000001bc42010e00:控制字节数组的起始地址。
growth_left = 0:哈希表已经满了。
items = 3:哈希表中有 3 个键值对 

这里面只有一个地址,就从这里开始

读取桶的状态

memory read -c 4 0x000001bc42010e00

memory read 是lldb命令,-c 4 表示读取4个字节的内容

为什么是4 ,因为有4个桶。

关于桶的状态如下

状态描述控制字节值备注
占用 (Occupied)桶中存储了有效的键值对< 0x80 (高位为 0)通常为哈希值的高 7 位 (hash >> 57),用于快速比较哈希
空 (Empty)桶未被使用,当前没有键值对0x80 (二进制 10000000)表示桶从未存储数据
已删除 (Tombstone)桶曾存储键值对,但数据已被删除0xff (二进制 11111111)用于标记移除的条目,避免破坏哈希表的探查链 (probe chain)

运行结果如下

(lldb) memory read -c 4 0x000001bc42010e00
0x1bc42010e00: 70 ff 06 7f   

70 是小于80,因此是占有,索引为1 。

ff  表示曾经占有 ,

06 是小于80,因此是占有 ,索引为3。

7f 是小于80,因此是占有,索引为4 。

获取键值对

怎么获取键值对,这确实是一个的问题?

笔者添加一个循环,打上断点

    for (k,v) in &map{
        println!("{:?},{:?}",k,v);
    }

看看k,v

此时,地址发生变换,重新执行上面的命令

(lldb) memory read -c 4 0x000002a0336c0e00
0x2a0336c0e00: ff 7a 75 4c

发现了变换,还是三个,但是索引是2,3,4了 

ctrl指针的地址是0x2a0336c0e00,笔者复制出来,没有前面的0,不影响

k的地址是          0x2a0336c0dc0

二者之差,等于0x40,变成十进制就是64

有这样一个公式

桶的起始地址 = 桶数组的起始地址 + (桶索引 × 桶的大小)

现在有两个地址,有索引2,笔者的电脑是64位的

&str有两个部分组成,一个指向数据的地址,占8个字节,还有一个是长度u64,也是8个字节,因此,一个&str,占16个字节

同时考虑到键值对都是&str,因此桶的大小为32字节

实际上可以使用std::mem

use std::mem;
println!("(&str, &str) {}", mem::size_of::<(&str, &str)>());

结果如下

(&str, &str) 32

没问题 

同时考虑到索引为2 ,因此2乘以32等于64,两个地址之差正是64

没问题。

经过多次尝试。可以发现

通过ctrl的pointer的地址,减去 (桶索引 × 桶的大小),就可以算出一个地址。这个地址就是键值对的指针地址

键值对的指针地址

再次运行命令,获取索引

现在ctrl pointer的地址变成了0x00000277412c0e00

(lldb) memory read -c 4 0x00000277412c0e00
0x277412c0e00: 17 ff 5d 04

索引为1,3,4

因此0x00000277412c0e00-32=0x00000277412c0de0

同时考虑到笔者是64位的电脑,读取一个指针需要8个字节

因此

(lldb) memory read -c 8 0x00000277412c0de0
0x277412c0de0: 9c 1b e2 61 f6 7f 00 00

这就是指针的地址

笔者的操作系统储存数据的方式是小端序,因此

指针地址为

0x00007ff661e21b9c

此时,读取数据

(lldb) memory read -c 5 0x00007ff661e21b9c
0x7ff661e21b9c: 36 36 31 32 33                                   66123 

为什么读取5个,这是笔者主动选择的,

看到66123,

结合代码,66是键,123是值,读取到键值对

读取索引4的键值对

0x00000277412c0e00 -4*32=0x00000277412c0d80

指针为

(lldb) memory read -c 8 0x00000277412c0d80
0x277412c0d80: a1 1b e2 61 f6 7f 00 00   

地址为0x00007ff661e21ba1

键值对

(lldb) memory read -c 4 0x00007ff661e21ba1
0x7ff661e21ba1: 6f 6b 6e 6f                                      okno

 没问题

看看是否可以修改

(lldb) memory region 0x00007ff661e21ba1
[0x00007ff661e00000-0x00007ff661e2c000) r--

发现不可以修改。没有写的权限

多添加几个键值对

    map.insert("go", "tt");
    map.insert("66", "123");
    map.insert("ok", "no");
    map.insert("hello1","world1");
    map.insert("hello2","world2");
    map.insert("hello3","world3");
    map.insert("hello4","world4");
    map.insert("hello5","world5");

p map的结果如下

(lldb) p map
(std::collections::hash::map::HashMap<ref$<str$>,ref$<str$>,std::hash::random::RandomState>) map = {
  base = {
    hash_builder = (k0 = 12591853735628107626, k1 = 11397258469864990867)
    table = {
      table = {
        bucket_mask = 15
        ctrl = {
          pointer = 0x000002ea70e2c090
        }
        growth_left = 6
        items = 8
      }
      alloc = {}
      marker = {}
    }
  }
}

可以发现bucket_mask 变成了15,就有16个桶

因此,第一个内存的读取,桶的状态

(lldb) memory read -c 16 0x000002ea70e2c090
0x2ea70e2c090: 4a ff ff 0a 48 15 ff 60 ff 3c 2a ff ff 4d ff ff

索引分别是1、4、5、6、8、10、11、14

桶的大小依然是32,不妨读取索引8

地址为0x000002ea70e2c090 - 0x100 = 0x000002ea70e2bf90

指针地址为

(lldb) memory read -c 8 0x000002ea70e2bf90
0x2ea70e2bf90: c9 1b 48 e9 f6 7f 00 00 

即 0x00007ff6e9481bc9

(lldb) memory read -c 12 0x00007ff6e9481bc9
0x7ff6e9481bc9: 68 65 6c 6c 6f 34 77 6f 72 6c 64 34              hello4world4

看来是hello4这个键。其他同理

 使用i32作为键,&str作为值

看看桶的大小

println!("(i32,&str) {}", mem::size_of::<(i32, &str)>());

(i32,&str) 24

 i32是4字节,Rust 默认会按 8字节对齐,笔者是64位

因此8+16=24

插入键值对

    map.insert(1, "tt");

就一个

关键输出

 ctrl = {
          pointer = 0x0000013c22361450
        }

获取桶的状态

(lldb) memory read -c 4 0x0000013c22361450
0x13c22361450: ff ff ff 39       

索引为4

因此,key地址为

0x0000013c22361450 - 4*24

0x0000013c22361450- 0x60 = 0x000001d9c0da13f0

因为key是一个i32

此时这个地址——0x000001d9c0da13f0,就是key的地址

(lldb) p *(0x000001d9c0da13f0 as *mut u8)
(u8) * = 1

value的指针地址就是0x000001d9c0da13f8,不必细说

笔者重新运行。结果如下

(lldb) memory read -c 4 0x000001a895741880
0x1a895741880: ff ff ff 74   
(lldb)  p *(0x000001a895741820 as *mut i32)
(i32) * = 1
(lldb) memory read -c 8 0x000001a895741828
0x1a895741828: b8 1b 80 a2 f7 7f 00 00 
(lldb) memory read -c 2 0x00007ff7a2801bb8
0x7ff7a2801bb8: 74 74                                            tt

没问题

使用i32作为键,String作为值

首先,看看桶的大小

    let size= mem::size_of::<(i32, String)>();
    println!("size of (i32, String): {}", size);
size of (i32, String): 32

可以得到是32

 插入数据

    // 插入键值对
    map.insert(1, "tt".to_string());

先打印看看

    for (key, value) in &map {
        println!("key: {}, value: {}", key, value);
    }

结果如下 

key是*mut i32

value 是*mut alloc::string::String

key与value差了0x8

直接开始

(lldb) p map
(std::collections::hash::map::HashMap<i32,alloc::string::String,std::hash::random::RandomState>) map = {
  base = {
    hash_builder = (k0 = 14409132009700107725, k1 = 6608230683712741401)
    table = {
      table = {
        bucket_mask = 3
        ctrl = {
          pointer = 0x000001f8d3781470
        }
        growth_left = 2
        items = 1
      }
      alloc = {}
      marker = {}
    }
  }
}

ctrl的地址0x000001f8d3781470

看看桶的状态

(lldb) memory read -c 4 0x000001f8d3781470

0x1f8d3781470: 5d ff ff ff   

索引为1

因此 key 的地址0x000001f8d3781470-32=0x000001f8d3781450

(lldb) p *(0x000001f8d3781450 as *mut i32)

(i32) * = 1

没问题

key是i32,占8个字节,因此value的地址0x000001f8d3781458

同时考虑到value是一个字符串,在前面输出中是*mut alloc::string::String,

因此,笔者尝试

(lldb) p (0x000001f8d3781458 as *mut alloc::string::String)
(*mut alloc::string::String)  = 0x000001f8d3781458

没想到居然成功了

(lldb) p *(0x000001f8d3781458 as *mut alloc::string::String)
(alloc::string::String) * = {
  vec = {
    buf = {
      inner = {
        ptr = {
          pointer = {
            pointer = 0x000001f8d37861f0
          }
          _marker = {}
        }
        cap = (__0 = 2)
        alloc = {}
      }
      _marker = {}
    }
    len = 2
  }
}

那后面的事情就不必细说了。都有地址了。

(lldb) memory read -c 2 0x000001f8d37861f0
0x1f8d37861f0: 74 74                                            tt 

没问题

笔者感到很奇怪,为什么&str不行

如果改成&str,输出是这样的

但是

(lldb) p *(0x0000015a3ef96190 as *mut &str)

error: could not find type "str"

笔者不能理解。

但是对于i32为键, &str为值,获取到了key的地址

    map.insert(1,"ggggg");

比如

(lldb) p *(0x000001f296071820 as *mut i32)
(i32) * = 1

这个0x000001f296071820 就看key的地址

0x000001f296071820+0x8=0x000001f296071828 

0x000001f296071828 就是value的地址

而value是&str,包括两个部分,一个数据指针,一个是长度

总共是16个字节,因此,如下

(lldb) memory read -c 16 0x000001f296071828
0x1f296071828: b8 1b d2 34 f6 7f 00 00 05 00 00 00 00 00 00 00

前8个字节b8 1b d2 34 f6 7f 00 00 ,数据指针,05 就是长度

数据

(lldb) memory read -c 5 0x00007ff634d21bb8
0x7ff634d21bb8: 67 67 67 67 67                                   ggggg

长度,value的地址+8个字节,即

0x000001f296071828 +0x8=0x000001f296071830

(lldb) memory read -c 1 0x000001f296071830
0x1f296071830: 05                                               
(lldb) p *(0x000001f296071830 as *mut u8)
(u8) * = 5

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

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

相关文章

2025最新免费视频号下载工具!支持Win/Mac,一键解析原画质+封面

软件介绍 适用于Windows 2025 最新5月蝴蝶视频号下载工具&#xff0c;免费使用&#xff0c;无广告且免费&#xff0c;支持对原视频和封面进行解析下载&#xff0c;亲测可用&#xff0c;现在很多工具都失效了&#xff0c;难得的几款下载视频号工具&#xff0c;大家且用且珍…

Newton GPU 机器人仿真器入门教程(零)— NVIDIA、DeepMind、Disney 联合推出

系列文章目录 目录 系列文章目录 前言 一、快速入门 1.1 实时渲染 1.2 USD 渲染 1.3 示例&#xff1a;创建一个粒子链 二、重要概念 三、API 参考 3.1 求解器 3.1.1 XPBD 求解器 3.1.2 VBD 求解器 3.1.3 MuJoCo 求解器 3.2 关节控制模式 四、Newton 集成 4.1 Is…

【C++】学习、项目时Debug总结

这里写目录标题 1. 内存问题1.1. 内存泄漏1.1.1. 内存泄漏案例检查方法1.1.2. 主线程提前退出导致【控】1.1.3. PostThreadMessage失败导致的内存泄漏**【控】**1.1.4. SendMessage 时关闭客户端【控】1.1.5. 线程机制导致【**控】**1.1.6. exit&#xff08;0&#xff09;导致【…

26考研——中央处理器_指令流水线_指令流水线的基本概念 流水线的基本实现(5)

408答疑 文章目录 六、指令流水线指令流水线的基本概念流水线的基本实现流水线设计的原则流水线的逻辑结构流水线的时空图表示 八、参考资料鲍鱼科技课件26王道考研书 六、指令流水线 前面介绍的指令都是在单周期处理机中采用串行方法执行的&#xff0c;同一时刻 CPU 中只有一…

AI Agent-基础认知与架构解析

定义 AI Agent 可以理解为一种具备感知、决策和行动能力的智能实体&#xff0c;能够在复杂的环境中自主运行&#xff0c;并根据环境变化动态调整自身行为&#xff0c;以实现特定目标。与传统的人工智能程序相比&#xff0c;AI Agent 具有更强的自主性、交互性和适应性。它不仅能…

C语言--字符函数

C语言--字符函数 一、字符函数1.1 iscntrl1.2 isspace1.3 isdigit1.4 isxdigit1.5 islower1.6 isupper1.7 isalpha1.8 isalnum1.9 ispunct1.10 isgraph1.11 isprint 在编程的过程中&#xff0c;我们会经常处理字符&#xff0c;为了方便操作&#xff0c;C语言标准库中提供了一系…

菜鸟之路Day30一一MySQL之DMLDQL

菜鸟之路Day30一一MySQL之DML&DQL 作者&#xff1a;blue 时间&#xff1a;2025.5.8 文章目录 菜鸟之路Day30一一MySQL之DML&DQL一.DML0.概述1.插入语句&#xff08;insert&#xff09;2.更新语句&#xff08;update&#xff09;3.删除语句&#xff08;delete&#xf…

基 LabVIEW 的多轴电机控制系统

在工业自动化蓬勃发展的当下&#xff0c;多轴伺服电机控制系统的重要性与日俱增&#xff0c;广泛应用于众多领域。下面围绕基于 LabVIEW 开发的多轴伺服电机控制系统展开&#xff0c;详细阐述其应用情况。 一、应用领域与场景 在 3D 打印领域&#xff0c;该系统精确操控打印头…

《Go小技巧易错点100例》第三十二篇

本期分享&#xff1a; 1.sync.Map的原理和使用方式 2.实现有序的Map sync.Map的原理和使用方式 sync.Map的底层结构是通过读写分离和无锁读设计实现高并发安全&#xff1a; 1&#xff09;双存储结构&#xff1a; 包含原子化的 read&#xff08;只读缓存&#xff0c;无锁快…

需求分析阶段测试工程师主要做哪些事情

在软件测试需求分析阶段&#xff0c;主要围绕确定测试范围、明确测试目标、细化测试内容等方面开展工作&#xff0c;为后续测试计划的制定、测试用例的设计以及测试执行提供清晰、准确的依据。以下是该阶段具体要做的事情&#xff1a; 1. 需求收集与整理 收集需求文档&#x…

项目模拟实现消息队列第二天

消息应答的模式 1.自动应答: 消费者把这个消息取走了&#xff0c;就算是应答了&#xff08;相当于没有应答) 2.手动应答: basicAck方法属于手动应答(消费者需要主动调用这个api进行应答) 小结 1.需要实现生产者,broker server&#xff0c;消费者这三个部分的 2.针对生产者和消费…

5.Redission

5.1 前文锁问题 基于 setnx 实现的分布式锁存在下面的问题&#xff1a; 重入问题&#xff1a;重入问题是指 获得锁的线程可以再次进入到相同的锁的代码块中&#xff0c;可重入锁的意义在于防止死锁&#xff0c;比如 HashTable 这样的代码中&#xff0c;他的方法都是使用 sync…

dify 部署后docker 配置文件修改

1&#xff1a;修改 复制 ./dify/docker/.env.example ./dify/docker/.env 添加一下内容 # 启用自定义模型 CUSTOM_MODEL_ENABLEDtrue# 将OLLAMA_API_BASE_URL 改为宿主机的物理ip OLLAMA_API_BASE_URLhttp://192.168.72.8:11434# vllm 的 OPENAI的兼容 API 地址 CUSTOM_MODE…

数据结构——排序(万字解说)初阶数据结构完

目录 1.排序 2.实现常见的排序算法 2.1 直接插入排序 ​编辑 2.2 希尔排序 2.3 直接选择排序 2.4 堆排序 2.5 冒泡排序 2.6 快速排序 2.6.1 递归版本 2.6.1.1 hoare版本 2.6.1.2 挖坑法 2.6.1.3 lomuto前后指针 2.6.1.4 时间复杂度 2.6.2 非递归版本 2.7 归并排序…

快速入门深度学习系列(3)----神经网络

本文只针对图进行解释重要内容 这就是入门所需要掌握的大部分内容 对于不懂的名词或概念 你可以及时去查 对于层数 标在上面 对于该层的第几个元素 标在下面 输入层算作第0层 对于第一层的w b 参数 维度如下w:4*3 b:4*1 这个叫做神经元 比如对于第一层的神经元 这里说的很…

在线工具源码_字典查询_汉语词典_成语查询_择吉黄历等255个工具数百万数据 养站神器,安装教程

在线工具源码_字典查询_汉语词典_成语查询_择吉黄历等255个工具数百万数据 养站神器&#xff0c;安装教程 资源宝分享&#xff1a;https://www.httple.net/154301.html 一次性打包涵盖200个常用工具&#xff01;无论是日常的图片处理、文件格式转换&#xff0c;还是实用的时间…

Linux 阻塞和非阻塞 I/O 简明指南

目录 声明 1. 阻塞和非阻塞简介 2. 等待队列 2.1 等待队列头 2.2 等待队列项 2.3 将队列项添加/移除等待队列头 2.4 等待唤醒 2.5 等待事件 3. 轮询 3.1 select函数 3.2 poll函数 3.3 epoll函数 4. Linux 驱动下的 poll 操作函数 声明 本博客所记录的关于正点原子…

Java开发经验——阿里巴巴编码规范经验总结2

摘要 这篇文章是关于Java开发中阿里巴巴编码规范的经验总结。它强调了避免使用Apache BeanUtils进行属性复制&#xff0c;因为它效率低下且类型转换不安全。推荐使用Spring BeanUtils、Hutool BeanUtil、MapStruct或手动赋值等替代方案。文章还指出不应在视图模板中加入复杂逻…

机器人手臂“听不懂“指令?Ethercat转PROFINET网关妙解通信僵局

机器人手臂"听不懂"指令&#xff1f;Ethercat转PROFINET网关妙解产线通信僵局 协作机器人&#xff08;如KUKA iiWA&#xff09;使用EtherCAT控制&#xff0c;与Profinet主站&#xff08;如西门子840D CNC&#xff09;同步动作。 客户反馈&#xff1a;基于Profinet…

深度学习 CNN

CNN 简介 什么是 CNN&#xff1f; 卷积神经网络&#xff08;Convolutional Neural Network&#xff09;是专为处理网格数据&#xff08;如图像&#xff09;设计的神经网络。核心组件&#xff1a; 卷积层 &#xff1a;提取局部特征&#xff08;如边缘、纹理&#xff09;通过卷…