C/C++汇编学习(四)——编写不同的C++程序并分析其汇编输出

        我们可以从一个简单的C++代码示例开始,然后生成其对应的汇编代码并进行解析。这个过程不仅展示了C++代码如何被转换成汇编语言,而且还帮助理解编译器是如何处理代码的。

案例一

C++ 代码示例

        让我们使用一个简单的C++代码示例:一个计算两个数之和的函数。

#include <iostream>int add(int a, int b) {return a + b;
}int main() {int result = add(3, 4);std::cout << "The sum is: " << result << std::endl;return 0;
}

生成汇编代码

        要生成这段代码的汇编版本,你可以使用 g++ 编译器(或任何其他支持的C++编译器)并使用 -S 选项。例如:

g++ -S -o example.s example.cpp

汇编代码解析

        由于具体的汇编代码会根据编译器和目标架构的不同而有所差异,我将提供一个大致的解析,侧重于理解关键部分。

        伪代码:

# 伪代码,具体汇编代码可能不同
.globl _Z3addii    # add函数的全局标记
_Z3addii:          # add函数标签movl %edi, -4(%rbp)  # 将第一个参数移动到栈上movl %esi, -8(%rbp)  # 将第二个参数移动到栈上movl -4(%rbp), %edx  # 将第一个参数加载到寄存器movl -8(%rbp), %eax  # 将第二个参数加载到寄存器addl %edx, %eax      # 将两个参数相加ret                  # 返回结果.globl main       # main函数的全局标记
main:             # main函数标签# ...函数设置代码...movl $3, %edi  # 将3作为第一个参数movl $4, %esi  # 将4作为第二个参数call _Z3addii  # 调用add函数# ...打印结果代码...ret            # 返回

 

        实际汇编,Ubuntu22.04 g++11输出的汇编。

	.file	"c.cpp".text.local	_ZStL8__ioinit.comm	_ZStL8__ioinit,1,1.globl	_Z3addii.type	_Z3addii, @function
_Z3addii:
.LFB1731:.cfi_startprocendbr64pushq	%rbp.cfi_def_cfa_offset 16.cfi_offset 6, -16movq	%rsp, %rbp.cfi_def_cfa_register 6movl	%edi, -4(%rbp)movl	%esi, -8(%rbp)movl	-4(%rbp), %edxmovl	-8(%rbp), %eaxaddl	%edx, %eaxpopq	%rbp.cfi_def_cfa 7, 8ret.cfi_endproc
.LFE1731:.size	_Z3addii, .-_Z3addii.section	.rodata
.LC0:.string	"The sum is: ".text.globl	main.type	main, @function
main:
.LFB1732:.cfi_startprocendbr64pushq	%rbp.cfi_def_cfa_offset 16.cfi_offset 6, -16movq	%rsp, %rbp.cfi_def_cfa_register 6subq	$16, %rspmovl	$4, %esimovl	$3, %edicall	_Z3addiimovl	%eax, -4(%rbp)leaq	.LC0(%rip), %raxmovq	%rax, %rsileaq	_ZSt4cout(%rip), %raxmovq	%rax, %rdicall	_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc@PLTmovq	%rax, %rdxmovl	-4(%rbp), %eaxmovl	%eax, %esimovq	%rdx, %rdicall	_ZNSolsEi@PLTmovq	_ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_@GOTPCREL(%rip), %rdxmovq	%rdx, %rsimovq	%rax, %rdicall	_ZNSolsEPFRSoS_E@PLTmovl	$0, %eaxleave.cfi_def_cfa 7, 8ret.cfi_endproc
.LFE1732:.size	main, .-main.type	_Z41__static_initialization_and_destruction_0ii, @function
_Z41__static_initialization_and_destruction_0ii:
.LFB2235:.cfi_startprocendbr64pushq	%rbp.cfi_def_cfa_offset 16.cfi_offset 6, -16movq	%rsp, %rbp.cfi_def_cfa_register 6subq	$16, %rspmovl	%edi, -4(%rbp)movl	%esi, -8(%rbp)cmpl	$1, -4(%rbp)jne	.L7cmpl	$65535, -8(%rbp)jne	.L7leaq	_ZStL8__ioinit(%rip), %raxmovq	%rax, %rdicall	_ZNSt8ios_base4InitC1Ev@PLTleaq	__dso_handle(%rip), %raxmovq	%rax, %rdxleaq	_ZStL8__ioinit(%rip), %raxmovq	%rax, %rsimovq	_ZNSt8ios_base4InitD1Ev@GOTPCREL(%rip), %raxmovq	%rax, %rdicall	__cxa_atexit@PLT
.L7:nopleave.cfi_def_cfa 7, 8ret.cfi_endproc
.LFE2235:.size	_Z41__static_initialization_and_destruction_0ii, .-_Z41__static_initialization_and_destruction_0ii.type	_GLOBAL__sub_I__Z3addii, @function
_GLOBAL__sub_I__Z3addii:
.LFB2236:.cfi_startprocendbr64pushq	%rbp.cfi_def_cfa_offset 16.cfi_offset 6, -16movq	%rsp, %rbp.cfi_def_cfa_register 6movl	$65535, %esimovl	$1, %edicall	_Z41__static_initialization_and_destruction_0iipopq	%rbp.cfi_def_cfa 7, 8ret.cfi_endproc
.LFE2236:.size	_GLOBAL__sub_I__Z3addii, .-_GLOBAL__sub_I__Z3addii.section	.init_array,"aw".align 8.quad	_GLOBAL__sub_I__Z3addii.hidden	__dso_handle.ident	"GCC: (Ubuntu 11.4.0-1ubuntu1~22.04) 11.4.0".section	.note.GNU-stack,"",@progbits.section	.note.gnu.property,"a".align 8.long	1f - 0f.long	4f - 1f.long	5
0:.string	"GNU"
1:.align 8.long	0xc0000002.long	3f - 2f
2:.long	0x3
3:.align 8
4:

 

        注释解析 

.file   "c.cpp"  # 源文件名指定为 "c.cpp"
.text   # 开始代码段# 初始化部分
.local  _ZStL8__ioinit  # 声明静态初始化对象 _ZStL8__ioinit (由C++库使用)
.comm   _ZStL8__ioinit,1,1  # 为 _ZStL8__ioinit 分配空间# add 函数定义
.globl  _Z3addii  # 声明 add 函数为全局符号
.type   _Z3addii, @function  # 标记 _Z3addii 为函数类型
_Z3addii:  # add 函数的开始标记
.LFB1731:.cfi_startprocendbr64  # 结束分支保护指令pushq   %rbp  # 将基指针寄存器入栈,用于建立新的栈帧.cfi_def_cfa_offset 16.cfi_offset 6, -16movq    %rsp, %rbp  # 将栈顶指针复制到基指针寄存器,建立栈帧基址.cfi_def_cfa_register 6movl    %edi, -4(%rbp)  # 将函数第一个参数(a)从 edi 寄存器存储到栈上movl    %esi, -8(%rbp)  # 将函数第二个参数(b)从 esi 寄存器存储到栈上movl    -4(%rbp), %edx  # 将 a 从栈上加载到 edx 寄存器movl    -8(%rbp), %eax  # 将 b 从栈上加载到 eax 寄存器addl    %edx, %eax  # 将 edx 和 eax 中的值相加,结果存储在 eax 中 (实现 return a + b;)popq    %rbp  # 从栈上恢复原基指针寄存器的值.cfi_def_cfa 7, 8ret  # 返回到调用函数.cfi_endproc
.LFE1731:
.size   _Z3addii, .-_Z3addii  # 指定 add 函数的大小# 字符串常量定义
.section    .rodata  # 只读数据段
.LC0:.string "The sum is: "  # 存储字符串 "The sum is: " (对应 std::cout << "The sum is: ";).text   # 开始代码段
.globl  main  # 声明 main 函数为全局符号
.type   main, @function  # 标记 main 为函数类型
main:   # main 函数的开始标记
.LFB1732:.cfi_startprocendbr64  # 结束分支保护指令pushq   %rbp  # 将基指针寄存器入栈,用于建立新的栈帧.cfi_def_cfa_offset 16.cfi_offset 6, -16movq    %rsp, %rbp  # 将栈顶指针复制到基指针寄存器,建立栈帧基址.cfi_def_cfa_register 6subq    $16, %rsp  # 为局部变量分配16字节的栈空间movl    $4, %esi  # 将整数 4 放入 esi 寄存器,作为 add 函数的第二个参数movl    $3, %edi  # 将整数 3 放入 edi 寄存器,作为 add 函数的第一个参数call    _Z3addii  # 调用 add 函数 (对应 int result = add(3, 4);)movl    %eax, -4(%rbp)  # 将 add 函数返回值存储到栈上 (对应 int result = add(3, 4);)leaq    .LC0(%rip), %rax  # 加载字符串 "The sum is: " 的地址到 rax (对应 std::cout << "The sum is: ";)movq    %rax, %rsi  # 将字符串地址移动到第二参数寄存器leaq    _ZSt4cout(%rip), %rax  # 加载 std::cout 对象的地址到 raxmovq    %rax, %rdi  # 将 std::cout 地址移动到第一参数寄存器call    _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc@PLT  # 调用输出操作符函数 (对应 std::cout << "The sum is: ";)movq    %rax, %rdxmovl    -4(%rbp), %eax  # 将 add 函数的返回值加载到 eax (对应 std::cout << result;)movl    %eax, %esi  # 将返回值移动到第二参数寄存器movq    %rdx, %rdi  # 将 std::cout 对象地址移动到第一参数寄存器call    _ZNSolsEi@PLT  # 调用输出操作符函数 (对应 std::cout << result;)movq    _ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_@GOTPCREL(%rip), %rdx  # 加载 std::endl 地址 (对应 std::cout << std::endl;)movq    %rdx, %rsi  # 将 std::endl 地址移动到第二参数寄存器movq    %rax, %rdi  # 将 std::cout 对象地址移动到第一参数寄存器call    _ZNSolsEPFRSoS_E@PLT  # 调用输出操作符函数 (对应 std::cout << std::endl;)movl    $0, %eax  # 将 0 放入 eax 作为函数返回值 (对应 return 0;)leave  # 清理栈帧并返回.cfi_def_cfa 7, 8ret  # 返回到调用函数.cfi_endproc
.LFE1732:
.size   main, .-main  # 指定 main 函数的大小# 静态初始化部分
.type   _Z41__static_initialization_and_destruction_0ii, @function
...  # 静态初始化代码 (对应于C++全局和静态对象的构造和析构).section    .init_array,"aw"  # 初始化数组段
.align 8
.quad   _GLOBAL__sub_I__Z3addii  # 包含初始化函数指针.hidden __dso_handle
.ident  "GCC: (Ubuntu 11.4.0-1ubuntu1~22.04) 11.4.0"  # 编译器信息
.section    .note.GNU-stack,"",@progbits
.section    .note.gnu.property,"a"
.align 8
.long   1f - 0f
.long   4f - 1f
.long   5
0:.string "GNU"
1:.align 8.long   0xc0000002.long   3f - 2f
2:.long   0x3
3:.align 8
4:

        C语言版的汇编

#include <stdio.h>int add(int a, int b)
{return a+b;
}int main()
{int result = add(4,5);printf("result: %d\n", result);return 0;
}
	.file	"c.c".text.globl	add.type	add, @function
add:
.LFB0:.cfi_startprocendbr64pushq	%rbp.cfi_def_cfa_offset 16.cfi_offset 6, -16movq	%rsp, %rbp.cfi_def_cfa_register 6movl	%edi, -4(%rbp)movl	%esi, -8(%rbp)movl	-4(%rbp), %edxmovl	-8(%rbp), %eaxaddl	%edx, %eaxpopq	%rbp.cfi_def_cfa 7, 8ret.cfi_endproc
.LFE0:.size	add, .-add.section	.rodata
.LC0:.string	"result: %d\n".text.globl	main.type	main, @function
main:
.LFB1:.cfi_startprocendbr64pushq	%rbp.cfi_def_cfa_offset 16.cfi_offset 6, -16movq	%rsp, %rbp.cfi_def_cfa_register 6subq	$16, %rspmovl	$5, %esimovl	$4, %edicall	addmovl	%eax, -4(%rbp)movl	-4(%rbp), %eaxmovl	%eax, %esileaq	.LC0(%rip), %raxmovq	%rax, %rdimovl	$0, %eaxcall	printf@PLTmovl	$0, %eaxleave.cfi_def_cfa 7, 8ret.cfi_endproc
.LFE1:.size	main, .-main.ident	"GCC: (Ubuntu 11.4.0-1ubuntu1~22.04) 11.4.0".section	.note.GNU-stack,"",@progbits.section	.note.gnu.property,"a".align 8.long	1f - 0f.long	4f - 1f.long	5
0:.string	"GNU"
1:.align 8.long	0xc0000002.long	3f - 2f
2:.long	0x3
3:.align 8
4:

        G++与GCC区别

  1. 语言特性的差异

    • C语言不支持类和对象,因此在C的汇编代码中不会出现与类相关的构造函数、析构函数或成员函数调用。
    • C++支持更多的特性,如函数重载、模板、异常处理等,这些在C++生成的汇编代码中可能有所体现。
  2. 函数名称修饰(Name Mangling)

    • 在C++中,由于支持重载,函数名在编译时会经过修饰(Name Mangling)以包含更多信息(如参数类型等)。因此,C++生成的汇编代码中的函数名可能看起来更复杂。
    • C语言不支持重载,函数名称在汇编中保持原样。例如,这里的 add 函数在汇编中仍然是 add
  3. 标准库的使用

    • C++使用的是名为 iostream 的标准库进行输入输出,而C使用的是 stdio.h。因此,与输入输出相关的汇编代码会有所不同。
    • 在C++例子中,输出是通过 std::cout 实现的,而在C的示例中,是通过 printf 函数实现的。
  4. 汇编代码结构

    • 两种代码在汇编层面上的结构相似,因为它们都遵循类似的函数调用和栈管理规范。例如,都使用 pushq %rbpmovq %rsp, %rbp 来建立栈帧,使用 ret 返回等。
    • 在变量处理和函数调用方面,两者的汇编代码看起来非常相似。这是因为这些基础操作在C和C++中大致相同。
  5. 优化程度和编译器特定行为

    • 不同的编译器,甚至是同一编译器的不同版本或不同的编译选项,可能会产生不同的汇编代码。
    • 例如,某些优化可能会省略一些看似不必要的指令或重新排列指令的顺序。

        总结

  1. 函数调用和栈管理:在 addmain 函数中,我们看到了如何建立和拆除栈帧。这包括保存基指针寄存器、设置新的栈基址、以及为局部变量分配栈空间。理解这些操作有助于掌握函数调用的内部机制,这在汇编级别编程中非常重要。

  2. 参数传递和寄存器使用:观察 add 函数如何接收其参数(通过寄存器 ediesi),以及这些参数如何被移动到栈上和寄存器中。这揭示了编译器如何利用寄存器和栈来传递参数。

  3. 基本指令集:通过这个例子,我们了解到了一些基础的汇编指令,如 movl(数据移动)、addl(加法)、call(函数调用)、ret(返回)等,这些都是汇编语言的基础。

  4. 系统调用和库函数main 函数展示了如何使用库函数(如 std::cout)来执行I/O操作。这些操作在汇编层面转化为一系列 call 指令和寄存器操作。

  5. 符号名字修饰(Name Mangling):C++中函数和变量的名字在编译后经过修饰(mangling),以支持诸如重载等特性。例如,add 函数变成了 _Z3addii

  6. 静态和全局对象初始化:理解C++中静态和全局对象是如何在程序开始前初始化的,这部分在汇编代码中通过静态初始化函数和段来处理。

  7. 汇编与高级语言的关系:理解高级语言(如C++)代码如何转换为底层汇编指令,这对于深入理解计算机程序的工作方式非常关键。

  8. 调试和逆向工程:这种从高级语言到汇编的映射对于调试低级错误、进行性能优化以及逆向工程非常有用。

案例二

#include <iostream>class Point {
private:int x, y;public:Point() : x(0), y(0) {}  // 默认构造函数void setCoordinates(int newX, int newY) {x = newX;y = newY;}void printCoordinates() const {std::cout << "(" << x << ", " << y << ")" << std::endl;}
};int main() {Point point;point.setCoordinates(5, 3);point.printCoordinates();return 0;
}

汇编代码:

	.file	"c.cpp".text.local	_ZStL8__ioinit.comm	_ZStL8__ioinit,1,1.section	.text._ZN5PointC2Ev,"axG",@progbits,_ZN5PointC5Ev,comdat.align 2.weak	_ZN5PointC2Ev.type	_ZN5PointC2Ev, @function
_ZN5PointC2Ev:
.LFB1732:.cfi_startprocendbr64pushq	%rbp.cfi_def_cfa_offset 16.cfi_offset 6, -16movq	%rsp, %rbp.cfi_def_cfa_register 6movq	%rdi, -8(%rbp)movq	-8(%rbp), %raxmovl	$0, (%rax)movq	-8(%rbp), %raxmovl	$0, 4(%rax)noppopq	%rbp.cfi_def_cfa 7, 8ret.cfi_endproc
.LFE1732:.size	_ZN5PointC2Ev, .-_ZN5PointC2Ev.weak	_ZN5PointC1Ev.set	_ZN5PointC1Ev,_ZN5PointC2Ev.section	.text._ZN5Point14setCoordinatesEii,"axG",@progbits,_ZN5Point14setCoordinatesEii,comdat.align 2.weak	_ZN5Point14setCoordinatesEii.type	_ZN5Point14setCoordinatesEii, @function
_ZN5Point14setCoordinatesEii:
.LFB1734:.cfi_startprocendbr64pushq	%rbp.cfi_def_cfa_offset 16.cfi_offset 6, -16movq	%rsp, %rbp.cfi_def_cfa_register 6movq	%rdi, -8(%rbp)movl	%esi, -12(%rbp)movl	%edx, -16(%rbp)movq	-8(%rbp), %raxmovl	-12(%rbp), %edxmovl	%edx, (%rax)movq	-8(%rbp), %raxmovl	-16(%rbp), %edxmovl	%edx, 4(%rax)noppopq	%rbp.cfi_def_cfa 7, 8ret.cfi_endproc
.LFE1734:.size	_ZN5Point14setCoordinatesEii, .-_ZN5Point14setCoordinatesEii.section	.rodata
.LC0:.string	"("
.LC1:.string	", "
.LC2:.string	")".section	.text._ZNK5Point16printCoordinatesEv,"axG",@progbits,_ZNK5Point16printCoordinatesEv,comdat.align 2.weak	_ZNK5Point16printCoordinatesEv.type	_ZNK5Point16printCoordinatesEv, @function
_ZNK5Point16printCoordinatesEv:
.LFB1735:.cfi_startprocendbr64pushq	%rbp.cfi_def_cfa_offset 16.cfi_offset 6, -16movq	%rsp, %rbp.cfi_def_cfa_register 6subq	$16, %rspmovq	%rdi, -8(%rbp)leaq	.LC0(%rip), %raxmovq	%rax, %rsileaq	_ZSt4cout(%rip), %raxmovq	%rax, %rdicall	_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc@PLTmovq	%rax, %rdxmovq	-8(%rbp), %raxmovl	(%rax), %eaxmovl	%eax, %esimovq	%rdx, %rdicall	_ZNSolsEi@PLTmovq	%rax, %rdxleaq	.LC1(%rip), %raxmovq	%rax, %rsimovq	%rdx, %rdicall	_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc@PLTmovq	%rax, %rdxmovq	-8(%rbp), %raxmovl	4(%rax), %eaxmovl	%eax, %esimovq	%rdx, %rdicall	_ZNSolsEi@PLTmovq	%rax, %rdxleaq	.LC2(%rip), %raxmovq	%rax, %rsimovq	%rdx, %rdicall	_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc@PLTmovq	_ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_@GOTPCREL(%rip), %rdxmovq	%rdx, %rsimovq	%rax, %rdicall	_ZNSolsEPFRSoS_E@PLTnopleave.cfi_def_cfa 7, 8ret.cfi_endproc
.LFE1735:.size	_ZNK5Point16printCoordinatesEv, .-_ZNK5Point16printCoordinatesEv.text.globl	main.type	main, @function
main:
.LFB1736:.cfi_startprocendbr64pushq	%rbp.cfi_def_cfa_offset 16.cfi_offset 6, -16movq	%rsp, %rbp.cfi_def_cfa_register 6subq	$16, %rspmovq	%fs:40, %raxmovq	%rax, -8(%rbp)xorl	%eax, %eaxleaq	-16(%rbp), %raxmovq	%rax, %rdicall	_ZN5PointC1Evleaq	-16(%rbp), %raxmovl	$3, %edxmovl	$5, %esimovq	%rax, %rdicall	_ZN5Point14setCoordinatesEiileaq	-16(%rbp), %raxmovq	%rax, %rdicall	_ZNK5Point16printCoordinatesEvmovl	$0, %eaxmovq	-8(%rbp), %rdxsubq	%fs:40, %rdxje	.L6call	__stack_chk_fail@PLT
.L6:leave.cfi_def_cfa 7, 8ret.cfi_endproc
.LFE1736:.size	main, .-main.type	_Z41__static_initialization_and_destruction_0ii, @function
_Z41__static_initialization_and_destruction_0ii:
.LFB2239:.cfi_startprocendbr64pushq	%rbp.cfi_def_cfa_offset 16.cfi_offset 6, -16movq	%rsp, %rbp.cfi_def_cfa_register 6subq	$16, %rspmovl	%edi, -4(%rbp)movl	%esi, -8(%rbp)cmpl	$1, -4(%rbp)jne	.L9cmpl	$65535, -8(%rbp)jne	.L9leaq	_ZStL8__ioinit(%rip), %raxmovq	%rax, %rdicall	_ZNSt8ios_base4InitC1Ev@PLTleaq	__dso_handle(%rip), %raxmovq	%rax, %rdxleaq	_ZStL8__ioinit(%rip), %raxmovq	%rax, %rsimovq	_ZNSt8ios_base4InitD1Ev@GOTPCREL(%rip), %raxmovq	%rax, %rdicall	__cxa_atexit@PLT
.L9:nopleave.cfi_def_cfa 7, 8ret.cfi_endproc
.LFE2239:.size	_Z41__static_initialization_and_destruction_0ii, .-_Z41__static_initialization_and_destruction_0ii.type	_GLOBAL__sub_I_main, @function
_GLOBAL__sub_I_main:
.LFB2240:.cfi_startprocendbr64pushq	%rbp.cfi_def_cfa_offset 16.cfi_offset 6, -16movq	%rsp, %rbp.cfi_def_cfa_register 6movl	$65535, %esimovl	$1, %edicall	_Z41__static_initialization_and_destruction_0iipopq	%rbp.cfi_def_cfa 7, 8ret.cfi_endproc
.LFE2240:.size	_GLOBAL__sub_I_main, .-_GLOBAL__sub_I_main.section	.init_array,"aw".align 8.quad	_GLOBAL__sub_I_main.hidden	__dso_handle.ident	"GCC: (Ubuntu 11.4.0-1ubuntu1~22.04) 11.4.0".section	.note.GNU-stack,"",@progbits.section	.note.gnu.property,"a".align 8.long	1f - 0f.long	4f - 1f.long	5
0:.string	"GNU"
1:.align 8.long	0xc0000002.long	3f - 2f
2:.long	0x3
3:.align 8
4:

汇编伪代码:

# 类 Point 的默认构造函数
Point::Point():push rbp                   # 保存旧的基指针mov rbp, rsp               # 更新基指针mov [rbp-4], rdi           # 将 this 指针存储到局部变量mov rax, [rbp-4]           # 加载 this 指针mov dword ptr [rax], 0     # 将 x 初始化为 0mov dword ptr [rax+4], 0   # 将 y 初始化为 0pop rbp                    # 恢复基指针ret                        # 返回# 类 Point 的 setCoordinates 函数
Point::setCoordinates(int newX, int newY):push rbp                   # 保存旧的基指针mov rbp, rsp               # 更新基指针mov [rbp-4], rdi           # 将 this 指针存储到局部变量mov [rbp-8], esi           # 将 newX 存储到局部变量mov [rbp-12], edx          # 将 newY 存储到局部变量mov rax, [rbp-4]           # 加载 this 指针mov edx, [rbp-8]           # 加载 newXmov [rax], edx             # 更新 x 成员mov edx, [rbp-12]          # 加载 newYmov [rax+4], edx           # 更新 y 成员pop rbp                    # 恢复基指针ret                        # 返回# 类 Point 的 printCoordinates 函数
Point::printCoordinates() const:push rbp                   # 保存旧的基指针mov rbp, rsp               # 更新基指针mov [rbp-4], rdi           # 将 this 指针存储到局部变量mov rax, [rbp-4]           # 加载 this 指针mov ecx, [rax]             # 加载 x 成员mov edx, [rax+4]           # 加载 y 成员# 调用 std::cout 相关函数来输出 "(",x,", ",y 和 ")"pop rbp                    # 恢复基指针ret                        # 返回# main 函数
main:push rbp                   # 保存旧的基指针mov rbp, rsp               # 更新基指针sub rsp, 16                # 为 Point 对象分配栈空间lea rdi, [rbp-16]          # 将 Point 对象的地址放入 rdicall Point::Point          # 调用 Point 的构造函数lea rdi, [rbp-16]          # 将 Point 对象的地址放入 rdimov esi, 5                 # 将 5 作为 newX 参数放入 esimov edx, 3                 # 将 3 作为 newY 参数放入 edxcall Point::setCoordinates # 调用 setCoordinates 函数lea rdi, [rbp-16]          # 将 Point 对象的地址放入 rdicall Point::printCoordinates # 调用 printCoordinates 函数mov eax, 0                 # 将 0 放入 eax 作为返回值leave                      # 恢复栈和基指针ret                        # 返回

总结 

这个C++转汇编的案例为理解C++代码如何转换为底层机器码提供了重要的启示:

  1. 类构造和析构:理解类的构造函数和析构函数如何在汇编层面操作内存,尤其是如何初始化类成员变量,是深入理解对象生命周期的关键。

  2. 成员函数调用:通过分析成员函数的汇编代码,我们可以看到 this 指针是如何被处理和传递的。这有助于理解对象方法在内存中是如何与其数据成员关联的。

  3. 对象的内存布局:观察对象在内存中是如何布局的,特别是成员变量在对象内存结构中的位置。这对于理解面向对象编程的内存管理非常重要。

  4. 堆栈操作:类方法中的堆栈操作(如保存基指针、调整栈指针等)展示了函数调用的常见模式。理解这些模式对于深入学习函数的工作原理至关重要。

  5. 参数传递和寄存器使用:成员函数的参数传递、局部变量的处理以及寄存器的使用揭示了编译器如何优化函数调用和数据存储。

  6. 高级特性的底层实现:C++的高级特性(如类、对象、成员函数)在汇编层面的实现帮助我们理解这些特性的工作原理和潜在开销。

  7. 标准库函数的调用:如何在汇编中处理C++标准库函数的调用(例如,std::cout 的使用)。

  8. 调试和优化:对汇编代码的理解可以在调试时帮助更好地识别和修复底层的错误,并为性能优化提供线索。

 

未完待续

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

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

相关文章

Linux 命令汇总

Linux 命令 1.查找命令 find /home/gitlab-runner/videofusionbe/api/logs/ -name ‘04-08.log’ -print 2.更换用户命令 Sudo - gitlab-runner 3.使用停止现有项目 sh rsbc.sh stop 4.启动现有项目 sh rsbc.sh start 5.启动日志 tail -f Web.log.2022-04-21.log 6.…

【Java项目】实战CRUD的功能整理(持续更新)

目录 前言1. MybatisPlus功能2. Excel处理3. 其他 前言 在实战中学习&#xff0c;整理每个功能细节以及各个函数的使用 对于Java的细节可看我之前的专栏&#xff1a;Java专栏 1. MybatisPlus功能 完整的CRUD可看我之前的文章&#xff1a; Springboot整合MybatisPlus的基本…

linux安装nodejs

一&#xff0c;yum安装 yum -y install nodejs 二&#xff0c;下载安装包安装 官网下载地址&#xff1a;Download | Node.js 建议安装低版本的&#xff0c;安装高版本的会有很多依赖&#xff0c;处理起来非常麻烦&#xff0c;还浪费时间 [rootmaster1 local]# wget https://…

【ArcGIS微课1000例】0086:基于七普人口数据的人口密度分析与制图

本文基于七普人口数据(省份、地级市、县区三级)进行人口密度计算分析与制图。 文章目录 一、人口密度介绍二、数据准备三、密度计算1. 投影转换2. 密度计算四、密度制图四、注意事项一、人口密度介绍 人口密度(density of population)是单位土地面积上的人口数量。通常使用的计…

C++类成员的访问控制

前言 本篇文章介绍C类成员的访问控制 关键字 C的类成员通过使用三个关键字来对成员的访问进行控制&#xff0c;并且只有这三个关键字&#xff1a; publicprivateprotected 使用场景 上面的三个关键字会扮演两种角色&#xff0c;也就是说会有两种使用的地方&#xff1a; …

【数值分析】插值法,lagrange插值,牛顿插值

1. 插值法介绍 插值法是一种通过已知数据点来估计未知数据点的方法。它通过构建一个函数或曲线&#xff0c;使其经过已知数据点&#xff0c;从而在数据点之间进行估计或预测。插值法的基本思想是假设已知数据点之间存在某种规律或趋势&#xff0c;并利用这种规律来推断未知数据…

全解析阿里云Alibaba Cloud Linux镜像操作系统

Alibaba Cloud Linux是基于龙蜥社区OpenAnolis龙蜥操作系统Anolis OS的阿里云发行版&#xff0c;针对阿里云服务器ECS做了大量深度优化&#xff0c;Alibaba Cloud Linux由阿里云官方免费提供长期支持和维护LTS&#xff0c;Alibaba Cloud Linux完全兼容CentOS/RHEL生态和操作方式…

conda环境下Could not create share link解决方法

1 问题描述 在运行chatglm-6B项目时&#xff0c;运行python web_demo.py&#xff0c;出现如下错误&#xff1a; (chatglm) [rootlocalhost ChatGLM2-6B]# python web_demo.py Loading checkpoint shards: 100%|██████████████████████████████…

SwiftUI之深入解析如何创建一个灵活的选择器

一、前言 在 Dribbble 上找到的设计的 SwiftUI 实现时&#xff0c;可以尝试通过一些酷炫的筛选器扩展该项目以缩小结果列表。筛选视图将由两个独立的筛选选项组成&#xff0c;两者都有一些可选项可供选择。但是&#xff0c;在使用 UIKit 时&#xff0c;总是将这种类型的视图实…

Anaconda下调用ArcGIS的arcpy工具包

1.从64位切换到32位开发模式: set CONDA_FORCE_32BIT1 2. 创建2.7的环境 conda create -n py27 python2.7 3.将ArcGIS的目录C:\Python27\ArcGIS10.1\Lib\site-packages\Desktop10.1.pth直接拷贝到Anaconda3的目录里&#xff08;C:\Users\xxx\Anaconda3\Lib\site-packages&a…

RK3568 学习笔记 : ubuntu 20.04 下 Linux-SDK 镜像烧写

前言 开发板&#xff1a;【正点原子】ATK-DLRK3568 开发板&#xff0c;编译完 Linux-SDK 后&#xff0c;生成了相关的镜像文件&#xff0c;本篇记录一下 镜像烧写&#xff0c;当前编译环境在 VMware 虚拟机中&#xff0c;虚拟机系统是 ubuntu 20.04 此次烧写还算顺利&#xff…

Callback Hook

一、Callback Hook 函数名&#xff1a;useCallback 用于得到一个固定引用值的函数&#xff0c;通常用它进行性能优化。 useCallback: 该函数只需要传入两个参数&#xff1a;一个回调函数和一个依赖数组即可。 1.函数&#xff0c;useCallback会固定该函数的引用&#xff0c;…

[USACO04OPEN] The Cow Lineup

题目描述 约翰的 N &#xff08; 1 ≤ N ≤ 100000 &#xff09; N &#xff08; 1 \leq N \leq 100000 &#xff09; N&#xff08;1≤N≤100000&#xff09; 只奶牛站成了一列。每只奶牛都写有一个号牌&#xff0c;表示她的品种&#xff0c;号牌上的号码在 1 … K &#x…

【C++】知识点汇总(上)

C知识点复习上 一、C 概述1. 基本数据类型2. 变量定义和访问3. 常量与约束访问 二、程序控制结构详解与示例1. 表达式2. 选择控制2.1 if 语句2.2 switch 语句 3. 循环控制3.1 for 循环3.2 while 循环3.3 do-while 循环 4. goto 语句5. 控制语句的嵌套 三、函数1. 函数的定义和调…

搜索二维矩阵 II(LeetCode 240)

1.问题描述 编写一个高效的算法来搜索 m x n 矩阵 matrix 中的一个目标值 target 。该矩阵具有以下特性&#xff1a; 每行的元素从左到右升序排列。每列的元素从上到下升序排列。 示例&#xff1a; 输入&#xff1a;matrix [[1,4,7,11,15],[2,5,8,12,19],[3,6,9,16,22],[10…

快速、准确地检测和分类病毒序列分析工具 ViralCC的介绍和详细使用方法,fudai shiyong ijaoben

介绍 viralcc是一个基因组病毒分析工具&#xff0c;可以用于快速、准确地检测和分类病毒序列。 github&#xff1a;dyxstat/ViralCC: ViralCC: leveraging metagenomic proximity-ligation to retrieve complete viral genomes (github.com) Instruction of reproducing resul…

ubuntu的boot分区被删除恢复

在鼓捣黑苹果的时候&#xff0c;误删了ubuntu的boot分区&#xff0c;进系统的时候出现emergency mode&#xff0c;那么现在来讲讲怎么恢复 首先做一个ubuntu的启动盘&#xff0c;然后进入启动盘的系统选择试用 呼出命令行&#xff0c;然后添加一个源 sudo add-apt-repository…

207.【2023年华为OD机试真题(C卷)】小朋友来自多少小区(贪心算法实现-JavaPythonC++JS实现)

🚀点击这里可直接跳转到本专栏,可查阅顶置最新的华为OD机试宝典~ 本专栏所有题目均包含优质解题思路,高质量解题代码(Java&Python&C++&JS分别实现),详细代码讲解,助你深入学习,深度掌握! 文章目录 一. 题目-小朋友来自多少小区二.解题思路三.题解代码Pyt…

微众区块链观察节点的架构和原理 | 科普时间

践行区块链公共精神&#xff0c;实现更好的公众开放与监督&#xff01;2023年12月&#xff0c;微众区块链观察节点正式面向公众开放接入功能。从开放日起&#xff0c;陆续有多个观察节点在各地运行&#xff0c;同步区块链数据&#xff0c;运行区块链浏览器观察检视数据&#xf…

flutter项目用vscode打包apk包,完美运行到手机上

1.创建密钥库 执行以下命令: keytool -genkey -v -keystore F:/key.jks -keyalg RSA -keysize 2048 -validity 10000 -alias key 生成 2,048 位RSA密钥对和自签名证书 (SHA256withRSA) (有效期为 10,000 天) 2.填写密钥内容 执行以上命令后会提示一次输入密钥库密码、确认…