分析常见数据结构在内存中的存储形式

news2025/1/6 20:11:43

本文会在x64dbg中分析vector,list,map的内存存储特点

目录

分析vector在内存中的存储形式

x32dbg分析vector数组

总结一下vector的内存布局

分析 list 在内存中的存储形式

x32dbg分析 list 数组

总结一下 list 的内存布局

分析map在内存中的存储形式

x32dbg分析map

总结一下map的内存布局

vector list map的对比


分析vector在内存中的存储形式

vector:C++里面的动态数组,通过一个连续的数组存放元素,如果集合已经满了,在新增数据的时候,就要分配一块更大的内存,将原来的数据复制过来,释放之前的内存,再插入新的元素。

测试代码

#include <windows.h>
#include <vector>
using namespace std;
int main()
{
	vector<int> myVec;;
	myVec.push_back(1);
	myVec.push_back(2);
	myVec.push_back(3);
	myVec.push_back(4);
	myVec.push_back(5);
	myVec.push_back(6);
	myVec.push_back(7);
	return 0;
}

使用IDA加载符号文件,并导出map文件,map文件里面保存的是地址对应的函数或者变量名,然后将map文件导入到x64dbg

File--Produce File--Create map file  全部勾选

在x64dbg中导入map文件,使用插件SwissArmyKnife加载map文件

x32dbg分析vector数组

00EFF95C这个地址是vector的首地址

第一个函数是给是给vector分配内存空间并初始化,这里其实就标识了vector数组使用的内存空间是0x10个字节。

第二个函数就对应源码的vector数组初始化。这里将+0偏移的内存写入了一个地址,跟随地址里的内容就能发现实际上这个字段是一个指向自己的指针,有点类似string的结构,两个都是STL结构,那么有一个相同的字段也就说的通了。

对应的是  vector<int> myVec;

压入七个元素后,查看vector的内存:

第一个地址存储一个地址,这个地址存储的值是00EFF95C,这是个指针

第二个地址存储一个地址,指向vector数组的首个元素地址

第三个地址存储一个地址,执行vector数组最后一个元素的下一个地址

最后一个地址存储一个地址,指向的是整个vector缓冲区的结束地址

观察存储数据的空间

+8 指向的是 CDCDCD +0x10 指向的是 0xFDFDFDFD CD 是堆空间刚刚开辟出来的时候的一个默认 值,这一块区域其实也是vector 提前申请好的堆空间,只不过还没有使用。

在复制的过程中,vector会根据需要的空间大小去动态的申请内存,把原来的数据拷贝一份,释放掉之前的内存。

总结一下vector的内存布局

如果我们想计算出动态数组的长度,只需要用第三个地址减去第二个地址,然后除以元素的大小,就能 算出元素的个数了。
template<class T>
struct my_vector
{
    my_vector* self; // 指向自己的指针
    T* fisrt; // 指向数据的首地址
    T* last; // 最后一个元素的下一个位置(数据结束地址)
    T* end; // 指向缓冲区的结尾
};

分析 list 在内存中的存储形式

C++ 里的 list ,是一个双向链表,链表这种数据结构的优点在于插入数据和删除数据快,但是查找数据慢,list vector 的优缺点其实是互补的。测试代码

#include <windows.h>
#include <list>
using namespace std;
int main()
{
printf("hello guishou");
list<int> myList;
myList.push_back(1);
myList.push_back(2);
myList.push_back(3);
myList.push_back(4);
myList.push_back(5);
myList.push_back(6);
myList.push_back(7);
return 0;
}

x32dbg分析 list 数组

第一个是初始化函数,给List数据结构分配内存空间,并初始化。这里我们可以看出list这个数据结构实 际上是占了0xC个字节

第二个构造函数执行完成之后,前两个地址就已经被赋值了

存入所有数据

Listd对象

  1. 第一个地址存储一个指针指向自己
  2. 第二地址指向链表头
  3. 第三个地址存储链表由几个节点

链表头

第一个头节点,第二个尾节点

压入所有元素

List对象

链表头

节点一

第一个地址指向下一个地址

第二个地址指向上一个地址

顺着链表头第一个地址走下去会重新返回来

总结一下 list 的内存布局

template <class T>
struct my_list
{
    my_list* self; // 指向自己的指针
    my_list_node<T>* header; // 指向头节点,头节点不存储数据
    int size; // 元素个数
};
template <class T>
struct my_list_node
{
    my_list_node* next; // 下一个节点
    my_list_node* prev; // 上一个节点
    T data; // 数据域
};

分析map在内存中的存储形式

C++map的实现是红黑树, 红黑树是一种自平衡二叉查找树,这种数据结构是在进行插入和删除的时候 进行特定的操作来保持二叉查找树的平衡,从而获得较高的查找性能。
map存储数据是以键值对的方式进行存储的: key-value
优点:查找数据效率高
缺点:插入数据效率低

如上图,如果你把这一堆的数据插入到 map 容器中,不管你使用的是什么插入顺序,最后的树结构都是 这样。这样做的好处在于,把所有的元素维持了一个有顺序的树的结构,查找效率高。
比如我想要找 40 ,先和 50 比较,比 50 小,那么就来左边,比 35 大,那么就来 35 的右边,然后比 45 小, 来到45 的右边。这样的话,我只需要查找三次就能找到我要的数据,但是如果换成了链表和数组,那么就要一个一个去遍历对比。这就是红黑树的绝对优势。
测试代码
#include <windows.h>
#include <map>
using namespace std;
int main()
{
    printf("hello guishou");
    map<int, int> myMap;
    myMap.insert(make_pair(1, 0x11));
    myMap.insert(make_pair(2, 0x22));
    myMap.insert(make_pair(3, 0x33));
    myMap.insert(make_pair(4, 0x44));
    return 0;
}

x32dbg分析map

第一个函数初始化,这里可以确定map这个数据结构是0xC个字节

第二个函数

map对象,第一个地址是指针,指向自己;第二个地址指向头节点;第三个存储元素个数;

头节点

010BEFA8是1号左子节点,010BA0E0是父节点,010C2C88是右子节点

其中 CDCD0101 表示的是整个二叉树的头节点

节点

+00 左节点
+04 父节点
+08 右节点
+0xC 标志->是否是叶子节点 是否是头节点
+0x10 key
+0x14 value

总结一下map的内存布局

map对象:

+00 指针,指向自己

+04 头节点

+08 元素个数

头节点:

+00 左节点

+04 父节点

+08 右节点

+0C CDCD0101表示头节点  CDCD0001 表示子节点

+10 key

+14 value

节点:

+00 左节点
+04 父节点
+08 右节点
+0xC 标志->是否是叶子节点 是否是头节点
+0x10 key
+0x14 value

vector list map的对比

vector对于随机访问的速度很快,但是对于插入元素的速度很慢,他需要拷贝和移动整个数据。vector适合高效存取,如果你不在乎插入和删除的效率的话可以使用。


list 对于随机访问速度很慢,因为他需要遍历整个链表,但是对于插入和删除速度快,只需要改变指针的指向就可以了。list适合频繁插入和删除,很少查找的场景


map查找速度最快,即使数据量到了百万级也是毫秒级别的,但是他的查找性能是以插入数据时维护底层红黑树作为代价的——即插入数据比较耗时。大量数据插入的场景是不适合map的,性能非常低,比vector和list慢几倍甚至十几倍。map适合偶尔插入一条数据并且很少clear,又频繁查找的场景。比如说大量数据去重 用vector和list都要遍历,而map则不需要

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

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

相关文章

python爬虫爬取电影数据并做可视化

思路&#xff1a; 1、发送请求&#xff0c;解析html里面的数据 2、保存到csv文件 3、数据处理 4、数据可视化 需要用到的库&#xff1a; import requests,csv #请求库和保存库 import pandas as pd #读取csv文件以及操作数据 from lxml import etree #解析html库 from …

内网穿透工具 Cpolar 帮您实现用友U8 Cloud 的外网部署,一键畅享云端ERP

文章目录 前言1. 用户需求2. Cpolar内网穿透的安装和注册2.1 Cpolar云端设置2.2 Cpolar Web UI本地设置 3. 公网访问测试 前言 用友U8 Cloud是用友公司推出的一款云端ERP解决方案。它以云计算技术为基础&#xff0c;为企业提供全面的企业资源管理解决方案&#xff0c;涵盖了财…

主机存活检测脚本

原理演示 在命令行下用下面命令安装scap模块&#xff1a; python -m pip install scapyscapy与scrapy 有非常大的区别。 scapy 是一个Python 的第三方模块&#xff0c;被称为“网络神器”。scapy 模块能够发送、捕获、分析和铸造网络数据 sr1发送接收函数 如图&#xff0c;安…

AI绘画变现渠道:日入100+,推荐一个本人实操的方法

关于AI绘画变现&#xff0c;之前写了几篇相关的文章&#xff0c;需要的自己查阅&#xff1a; AI绘画&#xff1a;如何让图片开口说话生成视频&#xff1f;变现渠道有哪些&#xff1f; 无私分享我的AI绘画变现之路&#xff0c;普通人可实操可模仿 AI壁纸号一周增加上千粉丝&a…

二叉树题目:层数最深叶子结点的和

文章目录 题目标题和出处难度题目描述要求示例数据范围 解法一思路和算法代码复杂度分析 解法二思路和算法代码复杂度分析 题目 标题和出处 标题&#xff1a;层数最深叶子结点的和 出处&#xff1a;1302. 层数最深叶子结点的和 难度 4 级 题目描述 要求 给定一个二叉树…

有效的括号(栈的高频面试题)

一、题目描述 题目连接&#xff1a;有效的括号 给定一个只包括 (&#xff0c;)&#xff0c;{&#xff0c;}&#xff0c;[&#xff0c;] 的字符串 s &#xff0c;判断字符串是否有效。 有效字符串需满足&#xff1a; 左括号必须用相同类型的右括号闭合。左括号必须以正确的顺…

我的创作纪念日 · 开始创作的第128天~

我的创作纪念日 开始创作的第128天 1️⃣ 机缘2️⃣ 收获3️⃣ 日常4️⃣ 憧憬 1️⃣ 机缘 时光匆匆&#xff0c;春去秋来&#xff0c;2023年在CSDN下笔的128天已去&#xff0c;回想当初成为创作者的初心&#xff0c;现在的心境已截然不同。当时正值上家公司工作变动&#xf…

【大数据】Doris 构建实时数仓落地方案详解(二):Doris 核心功能解读

Doris 构建实时数仓落地方案详解&#xff08;二&#xff09;&#xff1a;Doris 核心功能解读 1.Doris 发展历程2.Doris 三大模型3.Doris 数据导入4.Doris 多表关联5.Doris 核心设计6.Doris 查询优化7.Doris 应对实时数仓的痛点 1.Doris 发展历程 Apache Doris 是由 百度 研发并…

华为云云耀云服务器L实例评测|用Python的Flask框架加Nginx实现一个通用的爬虫项目

&#x1f3c6;作者简介&#xff0c;黑夜开发者&#xff0c;CSDN领军人物&#xff0c;全栈领域优质创作者✌&#xff0c;CSDN博客专家&#xff0c;阿里云社区专家博主&#xff0c;2023年6月CSDN上海赛道top4。 &#x1f3c6;数年电商行业从业经验&#xff0c;AWS/阿里云资深使用…

QUIC协议报文解析(三)

在前面的两篇文字里我们简单介绍了QUIC的发展历史&#xff0c;优点以及QUIC协议的连接原理。本篇文章将会以具体的QUIC报文为例&#xff0c;详细介绍QUIC报文的结构以及各个字段的含义。 早期QUIC版本众多&#xff0c;主要有谷歌家的gQUIC&#xff0c;以及IETF致力于将QUIC标准…

数据结构之堆的结构与实现

目录 一、堆的概念及结构 1.1堆的概念 1.2堆的性质 1.3堆的结构 二、堆的实现 2.1堆向下调整算法&#xff08;父亲与孩子做比较&#xff09; 2.2堆的向上调整算法&#xff08;孩子与父亲做比较&#xff09; 2.3堆的创建&#xff08;向下建堆&#xff09; 2.4向下建堆的时…

26 WEB漏洞-XSS跨站之订单及Shell箱子反杀记

目录 xss平台及工具使用session与Cookie获取问题演示案例某营销订单系统XSS盲打_平台某Shell箱子系统XSS盲打_工具其他参考应用案例-后台权限维持工具Http/s数据包提交Postman使用 xss平台及工具使用 凡是有数据交互的地方&#xff0c;前端是接收数据的&#xff0c;后端是要把…

Android Kotlin 高阶详解

前言 本文主要讲述kotlin高阶相关的内容&#xff0c;如果对kotlin基础还不了解的&#xff0c; 可以参考文章Android Kotlin 基础详解_袁震的博客-CSDN博客 1&#xff0c;与Java的相互调用 1.1在kotlin中调用java代码 大多数的java代码都可以直接在kotlin中调用&#xff0c…

Spring Cloud Alibaba Nacos注册中心(单机)

文章目录 Spring Cloud Alibaba Nacos注册中心&#xff08;单机&#xff09;1. docker 安装 nacos&#xff08;先别着急&#xff09;2. 配置nacos持久化到mysql、2.1 properties 文件 3. java注册3.1 POM文件3.2 properties文件3.3 测试配置中心 4.注册中心4.1 配置文件4.2测试…

【八大经典排序算法】选择排序

【八大经典排序算法】选择排序 一、概述二、思路解读三、代码实现&#xff08;升序&#xff09;四、优化&#xff08;升序&#xff09; 一、概述 选择排序作为一种简单直观的排序算法&#xff0c;最早由美国计算机科学家 Donald Knuth 在1968年提出。 选择排序的思想是将数组…

小程序从无到有教学教程-- 01.重置华为云服务器Huawei Cloud EulerOS 2.0版本并且设置安全组

概述 专门拿了专栏来讲解&#xff0c;所以目录结构就比较简单了 文章目录 概述修改华为云操作系统选择Huawei Cloud EulerOS 2.0 镜像顺便配置华为安全组 修改华为云操作系统 这里选择华为最新的系统&#xff0c;不过也就2.0~ 选择Huawei Cloud EulerOS 2.0 镜像 这里记住密…

企业架构LNMP学习笔记61

Nginx作为tomcat的前段反向代理&#xff1a; 在实际业务环境中&#xff0c;用户是直接通过域名访问&#xff0c;基于协议一般是http、https等。默认tomcat运行在8080端口。一般会通过前端服务器反向代理到后端的tomcat的方式&#xff0c;来实现用户可以通过域名访问tomcat的we…

bat写的git命令大全(适合初学者)掌握命令行下的Git操作!

欢迎来到Git&#xff01;无论你是一位Git初学者&#xff0c;这个在命令大全将帮助你在命令行下熟练运用Git&#xff0c;提高版本控制和团队协作的效率。从基本的仓库管理到分支操作&#xff0c;从提交修改到远程仓库同步&#xff0c;这个命令大全涵盖了Git的各种常用功能和技巧…

LeetCode-热题100-笔记-day31

105. 从前序与中序遍历序列构造二叉树https://leetcode.cn/problems/construct-binary-tree-from-preorder-and-inorder-traversal/ 给定两个整数数组 preorder 和 inorder &#xff0c;其中 preorder 是二叉树的先序遍历&#xff0c; inorder 是同一棵树的中序遍历&#xff0c…