C/C++汇编学习(六)——数据结构汇编实例:链表、树、图。

        我们继续开展

目录

一、链表

1. C++代码

2. 链表部分转为汇编并注释

二、树 

1. C++代码

2. 链表部分转为汇编并注释

3. 汇编伪代码

三、图

1. C++代码

2. 汇编伪代码

四、总结


一、链表

1. C++代码

#include <iostream>struct ListNode {int data;ListNode* next;ListNode(int x) : data(x), next(nullptr) {}
};int main() {// 创建链表的第一个节点ListNode* head = new ListNode(1);// 添加更多节点head->next = new ListNode(2);head->next->next = new ListNode(3);// 打印链表ListNode* current = head;while (current != nullptr) {std::cout << current->data << " ";current = current->next;}// 删除所有节点,释放内存current = head;while (current != nullptr) {ListNode* next = current->next;delete current;current = next;}return 0;
}

2. 链表部分转为汇编并注释

.file	"test.cpp"                ; 源文件信息,表示这个汇编代码是从 'test.cpp' 文件生成的.text                           ; 标记接下来的段落是代码段(文本段).local	_ZStL8__ioinit         ; 声明一个局部变量(通常是为了初始化C++标准库的I/O)
.comm	_ZStL8__ioinit,1,1      ; 分配并公共定义 _ZStL8__ioinit 变量.section	.text._ZN8ListNodeC2Ei,"axG",@progbits,_ZN8ListNodeC5Ei,comdat; 开始一个名为 .text._ZN8ListNodeC2Ei 的新段; "axG" 和 "@progbits" 是段的标志; _ZN8ListNodeC5Ei 是相关联的符号名.align 2                        ; 设置内存对齐,确保接下来的指令正确对齐以优化性能.weak	_ZN8ListNodeC2Ei       ; 将 _ZN8ListNodeC2Ei 符号声明为弱符号,允许它在其他地方被重定义.type	_ZN8ListNodeC2Ei, @function; 声明 _ZN8ListNodeC2Ei 是一个函数_ZN8ListNodeC2Ei:               ; 函数 _ZN8ListNodeC2Ei(ListNode的构造函数)的开始
.LFB1732:                       ; 局部函数开始的标签(由编译器生成).cfi_startproc             ; 指示调试器这是函数的开始endbr64                    ; ENDBR64指令,用于防止某些类型的攻击pushq	%rbp                ; 将基指针(rbp)压入栈,保存上一个函数的基指针.cfi_def_cfa_offset 16     ; 调整当前栈帧的偏移量(用于调试).cfi_offset 6, -16         ; 调整寄存器rbp的偏移量(用于调试)movq	%rsp, %rbp          ; 将栈指针(rsp)的值复制到基指针(rbp),建立新的栈帧.cfi_def_cfa_register 6    ; 设置当前帧的基指针寄存器为rbp(用于调试)movq	%rdi, -8(%rbp)     ; 将第一个参数(this指针,存储在rdi)移到栈帧中的局部变量位置movl	%esi, -12(%rbp)    ; 将第二个参数(构造函数的参数)移到栈帧中的另一个局部变量位置movq	-8(%rbp), %rax     ; 将this指针移回rax,准备用于后续操作movl	-12(%rbp), %edx    ; 将构造函数的参数移动到edx,准备用于后续操作movl	%edx, (%rax)       ; 将参数值(edx)存储在this指向的对象的data成员中movq	-8(%rbp), %rax     ; 再次将this指针移到raxmovq	$0, 8(%rax)        ; 将this指向的对象的next指针设置为nullptr(0)nop                       ; 空操作,没有实际效果,有时用于对齐popq	%rbp               ; 恢复原来的基指针(rbp).cfi_def_cfa 7, 8         ; 调整当前帧的堆栈指针(用于调试)ret                       ; 返回指令,结束函数.cfi_endproc              ; 指示调试器函数结束.LFE1732:                      ; 局部函数结束的标签(由编译器生成).size	_ZN8ListNodeC2Ei, .-_ZN8ListNodeC2Ei; 指定函数 _ZN8ListNodeC2Ei 的大小.weak	_ZN8ListNodeC1Ei   ; 将 _ZN8ListNodeC1Ei 符号声明为弱符号.set	_ZN8ListNodeC1Ei,_ZN8ListNodeC2Ei; 设置 _ZN8ListNodeC1Ei 符号等于 _ZN8ListNodeC2Ei(两个构造函数共享相同的代码).section	.rodata        ; 只读数据段的开始
.LC0:.string	" "             ; 存储一个字符串 " ".text                    ; 标记又回到代码段.globl	main             ; main函数声明为全局符号,使得链接器可以找到它.type	main, @function  ; 声明main是一个函数

        它展示了 ListNode 构造函数的汇编表示。这个构造函数初始化一个 ListNode 对象,设置其 data 成员并将 next 指针设为 nullptr。代码还包括了一些调试和链接相关的指令,这些通常是编译器自动生成的。

        理解汇编代码并转换为伪代码可以帮助更清楚地理解其操作。以下是基于您提供的汇编代码的伪代码表示,该代码对应于 ListNode 构造函数的实现:

ListNode Constructor (int value)
{// 假设 'this' 是指向当前ListNode对象的指针ListNode* this;// 函数开始Start Function// 保存基指针(rbp)并设置新的基指针Save Base PointerSet Base Pointer to Current Stack Pointer// 将构造函数的参数(value)和 'this' 指针保存到栈帧中Store 'value' to Stack FrameStore 'this' Pointer to Stack Frame// 将 'value' 赋值给当前对象的 'data' 成员this->data = value;// 将当前对象的 'next' 指针设置为 nullptrthis->next = nullptr;// 恢复基指针并结束函数Restore Base PointerEnd Function
}

二、树 

1. C++代码

#include <iostream>// TreeNode 结构体定义(根据您提供的代码)
struct TreeNode {int value;TreeNode *left;TreeNode *right;TreeNode(int x) : value(x), left(nullptr), right(nullptr) {}
};int main() {// 创建几个 TreeNode 实例TreeNode *root = new TreeNode(10);TreeNode *leftChild = new TreeNode(5);TreeNode *rightChild = new TreeNode(15);// 将子节点链接到根节点root->left = leftChild;root->right = rightChild;// 输出以验证结构std::cout << "Root Value: " << root->value << std::endl;std::cout << "Left Child Value: " << root->left->value << std::endl;std::cout << "Right Child Value: " << root->right->value << std::endl;// 释放分配的内存delete root->left;delete root->right;delete root;return 0;
}

2. 链表部分转为汇编并注释

.file	"test.cpp"                           # 指明源文件名称
.text                                       # 开始文本段(包含代码)
.local	_ZStL8__ioinit                      # 声明局部符号,这通常与C++标准库的初始化有关
.comm	_ZStL8__ioinit,1,1                   # 声明公共符号,用于C++标准库I/O的初始化# TreeNode 构造函数定义开始
.section	.text._ZN8TreeNodeC2Ei,"axG",@progbits,_ZN8TreeNodeC5Ei,comdat
.align 2                                    # 对齐指令,确保代码地址对齐以提高访问速度
.weak	_ZN8TreeNodeC2Ei                     # 标记构造函数为弱符号
.type	_ZN8TreeNodeC2Ei, @function          # 标记符号类型为函数
_ZN8TreeNodeC2Ei:                           # TreeNode 构造函数的入口点
.LFB1732:.cfi_startprocendbr64                                 # 结束分支保护,防止跳转到非法地址pushq	%rbp                            # 保存基指针.cfi_def_cfa_offset 16.cfi_offset 6, -16movq	%rsp, %rbp                      # 设置新的基指针.cfi_def_cfa_register 6movq	%rdi, -8(%rbp)                   # 将参数 this 指针保存在局部变量中movl	%esi, -12(%rbp)                  # 将参数 value 保存在局部变量中movq	-8(%rbp), %rax                   # 将 this 指针加载到 raxmovl	-12(%rbp), %edx                  # 将 value 加载到 edxmovl	%edx, (%rax)                     # 将 value 写入 this->valuemovq	-8(%rbp), %rax                   # 重新加载 this 指针到 raxmovq	$0, 8(%rax)                      # 初始化 this->left 为 nullptrmovq	-8(%rbp), %rax                   # 重新加载 this 指针到 raxmovq	$0, 16(%rax)                     # 初始化 this->right 为 nullptrnop                                    # 无操作指令(可能用于对齐)popq	%rbp                            # 恢复基指针.cfi_def_cfa 7, 8ret                                     # 从函数返回.cfi_endproc
.LFE1732:.size	_ZN8TreeNodeC2Ei, .-_ZN8TreeNodeC2Ei.weak	_ZN8TreeNodeC1Ei                   # 标记另一种构造函数形式(拷贝构造函数)为弱符号.set	_ZN8TreeNodeC1Ei,_ZN8TreeNodeC2Ei   # 将拷贝构造函数与普通构造函数关联# 字符串字面量的声明
.section	.rodata
.LC0:.string	"Root Value: "
.LC1:.string	"Left Child Value: "
.LC2:.string	"Right Child Value: "

3. 汇编伪代码

_ZN8TreeNodeC2Ei:                      ; TreeNode::TreeNode(int)
.LFB1732:.cfi_startprocendbr64                              ; 标记 ELF 可执行文件的入口点pushq %rbp                           ; 保存基指针.cfi_def_cfa_offset 16.cfi_offset 6, -16movq %rsp, %rbp                      ; 设置新的基指针.cfi_def_cfa_register 6movq %rdi, -8(%rbp)                  ; 将第一个参数(this 指针)存储在栈中movl %esi, -12(%rbp)                 ; 将第二个参数(int x)存储在栈中movq -8(%rbp), %rax                  ; 将 this 指针移到 raxmovl -12(%rbp), %edx                 ; 将 int x 移到 edxmovl %edx, (%rax)                    ; 将 x 的值存储在 this->valuemovq -8(%rbp), %rax                  ; 重新加载 this 指针到 raxmovq $0, 8(%rax)                     ; 设置 this->left 为 nullptrmovq -8(%rbp), %rax                  ; 再次加载 this 指针到 raxmovq $0, 16(%rax)                    ; 设置 this->right 为 nullptrnop                                  ; 无操作popq %rbp                            ; 恢复基指针.cfi_def_cfa 7, 8ret                                  ; 返回调用者.cfi_endproc
.LFE1732:

伪代码解释:

  1. 函数 _ZN8TreeNodeC2EiTreeNode 类构造函数的名称在 C++ 源代码中经过名字修饰(name mangling)后的结果。它接受一个整数参数(节点的值)。

  2. 函数开始时,保存当前函数的基指针,并将栈指针设置为新的基指针。

  3. this 指针(指向要初始化的对象)和整数参数(节点的值)被存储在栈中。

  4. this 指针和整数值分别加载到寄存器 raxedx

  5. 将节点的值赋给 this->value

  6. this->leftthis->right 指针设置为 nullptr

  7. 函数结束时,恢复之前的基指针并返回。

三、图

1. C++代码

#include <iostream>
#include <list>
#include <vector>// 定义顶点结构
class Vertex {
public:int data; // 顶点数据std::list<int> edges; // 边的列表,存储与此顶点相连的顶点的索引Vertex(int data) : data(data) {}
};// 定义图结构
class Graph {
public:std::vector<Vertex> vertices; // 存储所有顶点的向量// 添加顶点void addVertex(int data) {Vertex newVertex(data);vertices.push_back(newVertex);}// 添加边(无向)void addEdge(int start, int end) {vertices[start].edges.push_back(end);vertices[end].edges.push_back(start);}// 打印图的信息void printGraph() {for (int i = 0; i < vertices.size(); i++) {std::cout << "Vertex " << vertices[i].data << ": ";for (int edge : vertices[i].edges) {std::cout << edge << " ";}std::cout << "\n";}}
};int main() {Graph g;g.addVertex(1);g.addVertex(2);g.addVertex(3);g.addEdge(0, 1);g.addEdge(1, 2);g.addEdge(2, 0);g.printGraph();return 0;
}

2. 汇编伪代码

        下面是这些C++类和方法可能对应的汇编伪代码的概览。这里假设的环境是x86架构,且伪代码简化了许多复杂的细节。

; 假设C++类的方法被编译为全局函数
; 每个函数的实现取决于编译器如何处理对象、方法调用和STL容器; Vertex构造函数
_Vertex_constructor:; 假设 'this' 指针在 eax 中; 假设构造函数的参数在栈中或寄存器中mov [eax], data  ; 将数据存储到对象的'data'成员; 初始化 'edges' 列表; ...; Graph构造函数
_Graph_constructor:; 初始化 'vertices' 向量; ...; Graph::addVertex 方法
_Graph_addVertex:; 假设 'this' 指针和参数在寄存器或栈中; 创建一个新的 Vertex 对象; 将 Vertex 对象添加到 'vertices' 向量中; ...; Graph::addEdge 方法
_Graph_addEdge:; 假设 'this' 指针和参数在寄存器或栈中; 在 'vertices' 向量中找到对应的顶点; 更新顶点的 'edges' 列表; ...; Graph::printGraph 方法
_Graph_printGraph:; 假设 'this' 指针在某个寄存器中; 遍历 'vertices' 向量; 对于每个顶点,打印数据和边; ...; main 函数
_main:; 创建 Graph 对象call _Graph_constructor; 调用 addVertex 和 addEdge; 调用 printGraph; ...

        演示了如何将C++代码中的对象构造、方法调用和循环转换为汇编代码。请注意,实际汇编代码将取决于许多因素,包括具体的编译器、目标架构和优化设置。每个类的方法在汇编级别都会被转换成一系列对内存的操作、条件跳转和函数调用,而这些操作可能非常复杂,尤其是对于像std::vectorstd::list这样的STL容器。实际的汇编代码可能包含更多的细节和特定的内存管理逻辑。

四、总结

  1. 深入理解计算机工作原理:汇编语言提供了对计算机硬件操作的直接控制,这有助于更深入地理解计算机如何在最基础的层面上工作。了解数据结构在汇编级别的实现可以帮助你理解高级编程语言是如何被翻译成机器可以执行的指令的。

  2. 优化性能:在特定场景下,了解数据结构在汇编级别的表现可以帮助开发者编写更高效的代码。例如,在性能关键的应用中,了解底层细节可以帮助优化内存访问模式和计算过程,从而提高程序的执行效率。

  3. 逆向工程和安全分析:在逆向工程和软件安全分析中,了解数据结构的汇编实现是非常重要的。这有助于分析编译后的代码,理解软件的行为,发现潜在的漏洞,或进行恶意软件分析。

  4. 更好的调试能力:当调试低级别的问题(如内存损坏、性能瓶颈)时,理解汇编语言和数据结构的内部工作机制可以提供更全面的洞察。这使得能够更精准地定位问题所在,特别是当高级语言的调试器无法提供足够信息时。

  5. 学术和研究应用:在计算机科学和工程的研究领域,深入理解数据结构的底层实现是非常重要的。这种知识对于设计新的算法、计算模型或硬件架构非常有价值。

  6. 编译器设计:对于编译器开发者来说,理解数据结构在汇编级别的表示是核心技能之一。这有助于实现更有效的代码生成和优化策略。

        总的来说,虽然大多数应用程序开发者不需要直接使用汇编语言,但对其有一定的理解仍然是有价值的,尤其是在性能优化、底层系统设计、安全分析和调试等领域。

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

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

相关文章

vue 前端等比例压缩图片(再转换成文件后上传后端)

前端压缩图片总的来说还是转base64 然后等比例放小宽和高 这个是上次压缩图片的一个扩展 压缩完之后 再将base64 转成blob再转成文件然后再上传 一生要强的前端崽子&#xff08;后端不支持base64上传&#xff09; 自己改吧改吧 // 图片上传async changePic(e) {this.isshang…

【Docker】Docker基础

文章目录 安装使用帮助启动命令镜像命令容器命令 安装 # 卸载旧版本 sudo yum remove docker \docker-client \docker-client-latest \docker-common \docker-latest \docker-latest-logrotate \docker-logrotate \docker-engine # 设置存储库 sudo yum install -y yum-utils …

关于git使用的tips

前言 这里是一些git指令使用的tips&#xff0c;如果你作为初学者的话&#xff0c;我认为它将对你有所帮助。 常见指令 常见问题处理 1、使用git clone下载【huggingface.co】资源超时或无法请求问题 绝大多数情况是网络问题&#xff0c;首先如果是比较大的资源&#xff0c;你需…

数据库:如何取消mysql的密码

因为调试MySQL数据接口&#xff0c;总是需要输入密码很烦&#xff0c;所以决定取消mysql的root密码&#xff0c; 网上推荐的有两种方法&#xff1a; 1、mysql命令 SET PASSWORD FOR rootlocalhostPASSWORD(); 2、运行 mysqladmin 命令 mysqladmin -u root -p password …

C# 错误: 集合已修改,可能无法执行枚举操作

出错原因是使用了RemoveAt()函数移除了数据中的某一个数&#xff0c;导致数据发生了错位&#xff08;参考链接一&#xff09; 解决方案&#xff1a; 第一种解决方法&#xff1a;使用for循环 第二种解决方法&#xff1a;调用ToArray()方法&#xff0c;然后再进行foreach循环 …

vue设置height:100vh导致页面超出屏幕可以上下滑动

刚开始设置的height:100vh&#xff0c;就会出现如图的效果&#xff0c;会出现上下滚动 <template><view class"container">......</view> </template><style lang"scss">.container {height: 100vh;} </style> 解决方…

精确掌控并发:分布式环境下并发流量控制的设计与实现(一)

这是《百图解码支付系统设计与实现》专栏系列文章中的第&#xff08;10&#xff09;篇。 本篇主要讲清楚常用的并发流量控制方案&#xff0c;包括固定窗口、滑动窗口、漏桶、令牌桶、分布式消息中间件等&#xff0c;以及各种方案在支付系统不同场景下的应用。 在非支付场景&a…

故事机手机平板等智能硬件DVT阶段可靠性测试方法

DVT是什么 DVT是设计样品验证测试评审阶段&#xff0c;这个阶段要进行全面的&#xff0c;客观的测试&#xff0c; 主要测试项目包括&#xff1a;功能测试&#xff0c;安规测试&#xff0c;性能测试&#xff0c;合规测试&#xff08;兼容性&#xff09;&#xff0c;机械测试&am…

QT上位机开发(树形控件在地图软件中的应用)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 树形控件还是非常有用的&#xff0c;比如在选择文件的时候、选择目录的时候、以及选择同类型数据中某一个特定选项的时候。当然&#xff0c;对于ca…

JVM工作原理与实战(十一):双亲委派机制

专栏导航 JVM工作原理与实战 RabbitMQ入门指南 从零开始了解大数据 目录 专栏导航 前言 一、双亲委派机制 1.双亲委派机制详解 2.父类加载器 3.双亲委派机制的主要作用 二、双亲委派机制常见问题 总结 前言 ​JVM作为Java程序的运行环境&#xff0c;其负责解释和执行字…

STM32的FMC独立管理和控制外部存储器

在STM32中&#xff0c;FMC&#xff08;Flexible Memory Controller&#xff09;是一个功能强大的外部存储器控制器&#xff0c;用于管理和控制外部存储器设备&#xff0c;如SRAM、SDRAM、NOR Flash等。FMC允许将多个存储器设备连接到微控制器&#xff0c;并通过不同的片选线进行…

LLM之长度外推(一)| 基于位置编码的长度外推研究综述

论文&#xff1a;Length Extrapolation of Transformers: A Survey from the Perspective of Position Encoding地址&#xff1a;https://arxiv.org/abs/2312.17044 Transformer自诞生以来就席卷了NLP领域&#xff0c;因为它具有对序列中复杂依赖关系进行建模的优越能力。尽管基…

001 Golang-channel-practice

最近在练习并发编程。加上最近也在用Golang写代码&#xff0c;所以记录一下练习的题目。 第一道题目是用10个协程打印100条信息&#xff0c;创建10个协程。每个协程都会有自己的编号。每个协程都会被打印10次。 package mainimport ("fmt""strconv" )func …

1-07基本数据类型

一、概述 C语言的基本数据类型&#xff08;也叫内置数据类型&#xff09;在日常编程中被频繁使用&#xff0c;本章我们主要简单地介绍下 C 语言的基本数据类型。 基本数据类型主要包括&#xff1a;整数类型&#xff0c;浮点数类型和字符类型&#xff0c;其中字符类型也可以看…

gradient_checkpointing

点评&#xff1a;本质是减少内存消耗的一种方式&#xff0c;以时间或者计算换内存 gradient_checkpointing&#xff08;梯度检查点&#xff09;是一种用于减少深度学习模型中内存消耗的技术。在训练深度神经网络时&#xff0c;反向传播算法需要在前向传播和反向传播之间存储中…

前端系列:正则表达式RegExp详解

文章目录 正则创建匹配方法元字符字符集合边界分组数量词汇匹配模式RegExp 方法特性 正则创建 字面量创建 const str asdf123sds3234 const regexp /\d/g const res str.match(regexp) console.log(res) //[123, 3234]构造函数 const str asdf123asad23121 const regexp n…

[②C++ Boost]: Boost库编译,arm交叉编译方法

前言 Boost是十分实用的C库&#xff0c;如果想在arm环境下使用&#xff0c;就需要自己下载源码编译&#xff0c;本篇博客就记录下Boost库的编译方法。 下载Boost源码 Boost源码的下载路径可以使用&#xff1a;https://sourceforge.net/projects/boost/files/boost/ 编译 …

408重要数据结构+算法汇总——C语言手搓版(全)

该套代码&#xff0c;大学期间跟着网课一遍一遍打下来的&#xff0c;408大概就这些了&#xff0c;别的杂七杂八其实还有很多&#xff0c;遗憾的是&#xff0c;一直没有整理和归纳。导致一遍遍地学一遍遍地忘记。大四就快毕业了&#xff0c;研也考了。这里做个整理&#xff0c;算…

JavaScript小案例

烟花 html <body><div id"box"></div><script src"./move.js"></script><script src"./fire.js"></script> </body>js代码 fire.js function Fire(){// 获取box盒子this.box document.que…

虚幻UE 材质-材质编辑器节点2

上一篇&#xff1a;虚幻UE 材质-材质编辑器节点 1 上一篇文章对材质编辑器的部分节点做了讲解和对比较常用的功能做了展示 这篇文章继续对上一篇的文章进行补充 文章目录 前言一、ReflectionVector反射向量二、Material Parameter Collection材质参数集三、TwoSideSign和Vertex…