C/C++汇编学习(四)——编写不同的C++程序并分析其汇编输出
????????我们可以从一个简单的C++代码示例开始,然后生成其对应的汇编代码并进行解析。这个过程不仅展示了C++代码如何被转换成汇编语言,而且还帮助理解编译器是如何处理代码的。
目录
1. std::char_traits 的 length 方法
案例一 基础
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_startproc
endbr64
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
movl %edi, -4(%rbp)
movl %esi, -8(%rbp)
movl -4(%rbp), %edx
movl -8(%rbp), %eax
addl %edx, %eax
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE1731:
.size _Z3addii, .-_Z3addii
.section .rodata
.LC0:
.string "The sum is: "
.text
.globl main
.type main, @function
main:
.LFB1732:
.cfi_startproc
endbr64
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
subq $16, %rsp
movl $4, %esi
movl $3, %edi
call _Z3addii
movl %eax, -4(%rbp)
leaq .LC0(%rip), %rax
movq %rax, %rsi
leaq _ZSt4cout(%rip), %rax
movq %rax, %rdi
call _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc@PLT
movq %rax, %rdx
movl -4(%rbp), %eax
movl %eax, %esi
movq %rdx, %rdi
call _ZNSolsEi@PLT
movq _ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_@GOTPCREL(%rip), %rdx
movq %rdx, %rsi
movq %rax, %rdi
call _ZNSolsEPFRSoS_E@PLT
movl $0, %eax
leave
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE1732:
.size main, .-main
.type _Z41__static_initialization_and_destruction_0ii, @function
_Z41__static_initialization_and_destruction_0ii:
.LFB2235:
.cfi_startproc
endbr64
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
subq $16, %rsp
movl %edi, -4(%rbp)
movl %esi, -8(%rbp)
cmpl $1, -4(%rbp)
jne .L7
cmpl $65535, -8(%rbp)
jne .L7
leaq _ZStL8__ioinit(%rip), %rax
movq %rax, %rdi
call _ZNSt8ios_base4InitC1Ev@PLT
leaq __dso_handle(%rip), %rax
movq %rax, %rdx
leaq _ZStL8__ioinit(%rip), %rax
movq %rax, %rsi
movq _ZNSt8ios_base4InitD1Ev@GOTPCREL(%rip), %rax
movq %rax, %rdi
call __cxa_atexit@PLT
.L7:
nop
leave
.cfi_def_cfa 7, 8
ret
.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_startproc
endbr64
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
movl $65535, %esi
movl $1, %edi
call _Z41__static_initialization_and_destruction_0ii
popq %rbp
.cfi_def_cfa 7, 8
ret
.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_startproc
endbr64 # 结束分支保护指令
pushq %rbp # 将基指针寄存器入栈,用于建立新的栈帧
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp # 将栈顶指针复制到基指针寄存器,建立栈帧基址
.cfi_def_cfa_register 6
movl %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, 8
ret # 返回到调用函数
.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_startproc
endbr64 # 结束分支保护指令
pushq %rbp # 将基指针寄存器入栈,用于建立新的栈帧
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp # 将栈顶指针复制到基指针寄存器,建立栈帧基址
.cfi_def_cfa_register 6
subq $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 对象的地址到 rax
movq %rax, %rdi # 将 std::cout 地址移动到第一参数寄存器
call _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc@PLT # 调用输出操作符函数 (对应 std::cout << "The sum is: ";)
movq %rax, %rdx
movl -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, 8
ret # 返回到调用函数
.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_startproc
endbr64
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
movl %edi, -4(%rbp)
movl %esi, -8(%rbp)
movl -4(%rbp), %edx
movl -8(%rbp), %eax
addl %edx, %eax
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE0:
.size add, .-add
.section .rodata
.LC0:
.string "result: %d\n"
.text
.globl main
.type main, @function
main:
.LFB1:
.cfi_startproc
endbr64
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
subq $16, %rsp
movl $5, %esi
movl $4, %edi
call add
movl %eax, -4(%rbp)
movl -4(%rbp), %eax
movl %eax, %esi
leaq .LC0(%rip), %rax
movq %rax, %rdi
movl $0, %eax
call printf@PLT
movl $0, %eax
leave
.cfi_def_cfa 7, 8
ret
.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区别
-
语言特性的差异:
- C语言不支持类和对象,因此在C的汇编代码中不会出现与类相关的构造函数、析构函数或成员函数调用。
- C++支持更多的特性,如函数重载、模板、异常处理等,这些在C++生成的汇编代码中可能有所体现。
-
函数名称修饰(Name Mangling):
- 在C++中,由于支持重载,函数名在编译时会经过修饰(Name Mangling)以包含更多信息(如参数类型等)。因此,C++生成的汇编代码中的函数名可能看起来更复杂。
- C语言不支持重载,函数名称在汇编中保持原样。例如,这里的
add
函数在汇编中仍然是add
。
-
标准库的使用:
- C++使用的是名为
iostream
的标准库进行输入输出,而C使用的是stdio.h
。因此,与输入输出相关的汇编代码会有所不同。 - 在C++例子中,输出是通过
std::cout
实现的,而在C的示例中,是通过printf
函数实现的。
- C++使用的是名为
-
汇编代码结构:
- 两种代码在汇编层面上的结构相似,因为它们都遵循类似的函数调用和栈管理规范。例如,都使用
pushq %rbp
和movq %rsp, %rbp
来建立栈帧,使用ret
返回等。 - 在变量处理和函数调用方面,两者的汇编代码看起来非常相似。这是因为这些基础操作在C和C++中大致相同。
- 两种代码在汇编层面上的结构相似,因为它们都遵循类似的函数调用和栈管理规范。例如,都使用
-
优化程度和编译器特定行为:
- 不同的编译器,甚至是同一编译器的不同版本或不同的编译选项,可能会产生不同的汇编代码。
- 例如,某些优化可能会省略一些看似不必要的指令或重新排列指令的顺序。
? ? ? ? 总结
-
函数调用和栈管理:在
add
和main
函数中,我们看到了如何建立和拆除栈帧。这包括保存基指针寄存器、设置新的栈基址、以及为局部变量分配栈空间。理解这些操作有助于掌握函数调用的内部机制,这在汇编级别编程中非常重要。 -
参数传递和寄存器使用:观察
add
函数如何接收其参数(通过寄存器edi
和esi
),以及这些参数如何被移动到栈上和寄存器中。这揭示了编译器如何利用寄存器和栈来传递参数。 -
基本指令集:通过这个例子,我们了解到了一些基础的汇编指令,如
movl
(数据移动)、addl
(加法)、call
(函数调用)、ret
(返回)等,这些都是汇编语言的基础。 -
系统调用和库函数:
main
函数展示了如何使用库函数(如std::cout
)来执行I/O操作。这些操作在汇编层面转化为一系列call
指令和寄存器操作。 -
符号名字修饰(Name Mangling):C++中函数和变量的名字在编译后经过修饰(mangling),以支持诸如重载等特性。例如,
add
函数变成了_Z3addii
。 -
静态和全局对象初始化:理解C++中静态和全局对象是如何在程序开始前初始化的,这部分在汇编代码中通过静态初始化函数和段来处理。
-
汇编与高级语言的关系:理解高级语言(如C++)代码如何转换为底层汇编指令,这对于深入理解计算机程序的工作方式非常关键。
-
调试和逆向工程:这种从高级语言到汇编的映射对于调试低级错误、进行性能优化以及逆向工程非常有用。
案例二 类
#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_startproc
endbr64
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
movq %rdi, -8(%rbp)
movq -8(%rbp), %rax
movl $0, (%rax)
movq -8(%rbp), %rax
movl $0, 4(%rax)
nop
popq %rbp
.cfi_def_cfa 7, 8
ret
.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_startproc
endbr64
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
movq %rdi, -8(%rbp)
movl %esi, -12(%rbp)
movl %edx, -16(%rbp)
movq -8(%rbp), %rax
movl -12(%rbp), %edx
movl %edx, (%rax)
movq -8(%rbp), %rax
movl -16(%rbp), %edx
movl %edx, 4(%rax)
nop
popq %rbp
.cfi_def_cfa 7, 8
ret
.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_startproc
endbr64
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
subq $16, %rsp
movq %rdi, -8(%rbp)
leaq .LC0(%rip), %rax
movq %rax, %rsi
leaq _ZSt4cout(%rip), %rax
movq %rax, %rdi
call _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc@PLT
movq %rax, %rdx
movq -8(%rbp), %rax
movl (%rax), %eax
movl %eax, %esi
movq %rdx, %rdi
call _ZNSolsEi@PLT
movq %rax, %rdx
leaq .LC1(%rip), %rax
movq %rax, %rsi
movq %rdx, %rdi
call _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc@PLT
movq %rax, %rdx
movq -8(%rbp), %rax
movl 4(%rax), %eax
movl %eax, %esi
movq %rdx, %rdi
call _ZNSolsEi@PLT
movq %rax, %rdx
leaq .LC2(%rip), %rax
movq %rax, %rsi
movq %rdx, %rdi
call _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc@PLT
movq _ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_@GOTPCREL(%rip), %rdx
movq %rdx, %rsi
movq %rax, %rdi
call _ZNSolsEPFRSoS_E@PLT
nop
leave
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE1735:
.size _ZNK5Point16printCoordinatesEv, .-_ZNK5Point16printCoordinatesEv
.text
.globl main
.type main, @function
main:
.LFB1736:
.cfi_startproc
endbr64
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
subq $16, %rsp
movq %fs:40, %rax
movq %rax, -8(%rbp)
xorl %eax, %eax
leaq -16(%rbp), %rax
movq %rax, %rdi
call _ZN5PointC1Ev
leaq -16(%rbp), %rax
movl $3, %edx
movl $5, %esi
movq %rax, %rdi
call _ZN5Point14setCoordinatesEii
leaq -16(%rbp), %rax
movq %rax, %rdi
call _ZNK5Point16printCoordinatesEv
movl $0, %eax
movq -8(%rbp), %rdx
subq %fs:40, %rdx
je .L6
call __stack_chk_fail@PLT
.L6:
leave
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE1736:
.size main, .-main
.type _Z41__static_initialization_and_destruction_0ii, @function
_Z41__static_initialization_and_destruction_0ii:
.LFB2239:
.cfi_startproc
endbr64
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
subq $16, %rsp
movl %edi, -4(%rbp)
movl %esi, -8(%rbp)
cmpl $1, -4(%rbp)
jne .L9
cmpl $65535, -8(%rbp)
jne .L9
leaq _ZStL8__ioinit(%rip), %rax
movq %rax, %rdi
call _ZNSt8ios_base4InitC1Ev@PLT
leaq __dso_handle(%rip), %rax
movq %rax, %rdx
leaq _ZStL8__ioinit(%rip), %rax
movq %rax, %rsi
movq _ZNSt8ios_base4InitD1Ev@GOTPCREL(%rip), %rax
movq %rax, %rdi
call __cxa_atexit@PLT
.L9:
nop
leave
.cfi_def_cfa 7, 8
ret
.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_startproc
endbr64
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
movl $65535, %esi
movl $1, %edi
call _Z41__static_initialization_and_destruction_0ii
popq %rbp
.cfi_def_cfa 7, 8
ret
.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 初始化为 0
mov dword ptr [rax+4], 0 # 将 y 初始化为 0
pop 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] # 加载 newX
mov [rax], edx # 更新 x 成员
mov edx, [rbp-12] # 加载 newY
mov [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 对象的地址放入 rdi
call Point::Point # 调用 Point 的构造函数
lea rdi, [rbp-16] # 将 Point 对象的地址放入 rdi
mov esi, 5 # 将 5 作为 newX 参数放入 esi
mov edx, 3 # 将 3 作为 newY 参数放入 edx
call Point::setCoordinates # 调用 setCoordinates 函数
lea rdi, [rbp-16] # 将 Point 对象的地址放入 rdi
call Point::printCoordinates # 调用 printCoordinates 函数
mov eax, 0 # 将 0 放入 eax 作为返回值
leave # 恢复栈和基指针
ret # 返回
总结?
这个C++转汇编的案例为理解C++代码如何转换为底层机器码提供了重要的启示:
-
类构造和析构:理解类的构造函数和析构函数如何在汇编层面操作内存,尤其是如何初始化类成员变量,是深入理解对象生命周期的关键。
-
成员函数调用:通过分析成员函数的汇编代码,我们可以看到
this
指针是如何被处理和传递的。这有助于理解对象方法在内存中是如何与其数据成员关联的。 -
对象的内存布局:观察对象在内存中是如何布局的,特别是成员变量在对象内存结构中的位置。这对于理解面向对象编程的内存管理非常重要。
-
堆栈操作:类方法中的堆栈操作(如保存基指针、调整栈指针等)展示了函数调用的常见模式。理解这些模式对于深入学习函数的工作原理至关重要。
-
参数传递和寄存器使用:成员函数的参数传递、局部变量的处理以及寄存器的使用揭示了编译器如何优化函数调用和数据存储。
-
高级特性的底层实现:C++的高级特性(如类、对象、成员函数)在汇编层面的实现帮助我们理解这些特性的工作原理和潜在开销。
-
标准库函数的调用:如何在汇编中处理C++标准库函数的调用(例如,
std::cout
的使用)。 -
调试和优化:对汇编代码的理解可以在调试时帮助更好地识别和修复底层的错误,并为性能优化提供线索。
案例三 虚函数和继承
????????代码:
#include <iostream>
class Animal {
public:
virtual void makeSound() const {
std::cout << "Animal makes a sound" << std::endl;
}
};
class Dog : public Animal {
public:
void makeSound() const override {
std::cout << "Dog barks" << std::endl;
}
};
class Cat : public Animal {
public:
void makeSound() const override {
std::cout << "Cat meows" << std::endl;
}
};
void makeAnimalSound(const Animal& animal) {
animal.makeSound();
}
int main() {
Dog dog;
Cat cat;
makeAnimalSound(dog);
makeAnimalSound(cat);
return 0;
}
?汇编伪代码:
# 基类 Animal 的虚函数表
vtable_for_Animal:
.quad Animal::makeSound
# 类 Dog 的虚函数表
vtable_for_Dog:
.quad Dog::makeSound
# 类 Cat 的虚函数表
vtable_for_Cat:
.quad Cat::makeSound
# Animal 类的 makeSound 虚函数
Animal::makeSound:
push rbp
mov rbp, rsp
; 打印 "Animal makes a sound"
pop rbp
ret
# Dog 类的 makeSound 函数
Dog::makeSound:
push rbp
mov rbp, rsp
; 打印 "Dog barks"
pop rbp
ret
# Cat 类的 makeSound 函数
Cat::makeSound:
push rbp
mov rbp, rsp
; 打印 "Cat meows"
pop rbp
ret
# makeAnimalSound 函数
makeAnimalSound:
push rbp
mov rbp, rsp
sub rsp, 16
mov [rbp-16], rdi ; 存储 Animal 对象的引用
mov rax, [rbp-16] ; 加载 Animal 对象的引用
mov rax, [rax] ; 从对象的 vtable 地址中加载 makeSound 的地址
call rax ; 调用 makeSound 函数
add rsp, 16
pop rbp
ret
# main 函数
main:
push rbp
mov rbp, rsp
sub rsp, 32
; 创建 Dog 对象
lea rdi, [rbp-16]
call Dog::Dog ; 构造 Dog 对象
lea rdi, [rbp-16]
call makeAnimalSound
; 创建 Cat 对象
lea rdi, [rbp-32]
call Cat::Cat ; 构造 Cat 对象
lea rdi, [rbp-32]
call makeAnimalSound
add rsp, 32
pop rbp
ret
????????基于您提供的C++程序和汇编伪代码,我们可以分析如何在汇编层面实现继承和多态。这里的关键是虚函数表(VTable)的使用和间接函数调用。
伪代码解析
1. 虚函数表(VTable)
每个包含虚函数的类(如 Animal
, Dog
, Cat
)都有一个对应的虚函数表(VTable)。这个表包含了指向类的虚函数实现的指针。例如:
vtable_for_Animal
包含指向Animal::makeSound
函数的指针。vtable_for_Dog
包含指向Dog::makeSound
函数的指针。vtable_for_Cat
包含指向Cat::makeSound
函数的指针。
2. 对象构造和虚函数表指针
????????当创建一个对象时(例如 Dog
或 Cat
对象),构造函数会设置对象的虚函数表指针(vptr)指向相应类的虚函数表。例如:
lea rdi, [rbp-16] # 获取对象的内存地址
call Dog::Dog # 调用 Dog 的构造函数
????????在 Dog::Dog
构造函数内部,vptr 会被设置为指向 vtable_for_Dog
。?
3. 调用虚函数
????????当通过基类引用或指针调用虚函数时,程序使用对象的vptr 来确定应该调用哪个函数。例如,在 makeAnimalSound
函数中:
mov rax, [rbp-16] # 加载对象的地址
mov rax, [rax] # 从对象中获取 vptr(指向 VTable)
call [rax] # 通过 VTable 调用相应的 makeSound 函数
????????这里,[rax]
是对象的vptr,它指向对象的虚函数表。从该表中获取 makeSound
函数的地址并调用它。
4. 多态的实现
????????多态是通过在运行时解析函数调用来实现的。根据传递给 makeAnimalSound
的实际对象类型(Dog
或 Cat
),vptr 会指向不同的虚函数表,从而调用正确的 makeSound
方法:
- 如果传递的是
Dog
类型的对象,vptr 指向vtable_for_Dog
,调用的是Dog::makeSound
。 - 如果传递的是
Cat
类型的对象,vptr 指向vtable_for_Cat
,调用的是Cat::makeSound
。
总结
????????在汇编代码中,继承和多态主要通过对象的虚函数表指针(vptr)和虚函数表(VTable)来实现。构造函数负责设置vptr,而虚函数的调用则通过vptr和VTable间接完成。这使得程序能够在运行时根据对象的实际类型动态地选择正确的函数进行调用,实现多态。
实际汇编
.file "c.cpp"
.text
.local _ZStL8__ioinit
.comm _ZStL8__ioinit,1,1
.section .rodata
.LC0:
.string "Dog barks"
.section .text._ZNK3Dog9makeSoundEv,"axG",@progbits,_ZNK3Dog9makeSoundEv,comdat
.align 2
.weak _ZNK3Dog9makeSoundEv
.type _ZNK3Dog9makeSoundEv, @function
_ZNK3Dog9makeSoundEv:
.LFB1732:
.cfi_startproc
endbr64
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
subq $16, %rsp
movq %rdi, -8(%rbp)
leaq .LC0(%rip), %rax
movq %rax, %rsi
leaq _ZSt4cout(%rip), %rax
movq %rax, %rdi
call _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc@PLT
movq _ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_@GOTPCREL(%rip), %rdx
movq %rdx, %rsi
movq %rax, %rdi
call _ZNSolsEPFRSoS_E@PLT
nop
leave
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE1732:
.size _ZNK3Dog9makeSoundEv, .-_ZNK3Dog9makeSoundEv
.section .rodata
.LC1:
.string "Cat meows"
.section .text._ZNK3Cat9makeSoundEv,"axG",@progbits,_ZNK3Cat9makeSoundEv,comdat
.align 2
.weak _ZNK3Cat9makeSoundEv
.type _ZNK3Cat9makeSoundEv, @function
_ZNK3Cat9makeSoundEv:
.LFB1733:
.cfi_startproc
endbr64
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
subq $16, %rsp
movq %rdi, -8(%rbp)
leaq .LC1(%rip), %rax
movq %rax, %rsi
leaq _ZSt4cout(%rip), %rax
movq %rax, %rdi
call _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc@PLT
movq _ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_@GOTPCREL(%rip), %rdx
movq %rdx, %rsi
movq %rax, %rdi
call _ZNSolsEPFRSoS_E@PLT
nop
leave
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE1733:
.size _ZNK3Cat9makeSoundEv, .-_ZNK3Cat9makeSoundEv
.text
.globl _Z15makeAnimalSoundRK6Animal
.type _Z15makeAnimalSoundRK6Animal, @function
_Z15makeAnimalSoundRK6Animal:
.LFB1734:
.cfi_startproc
endbr64
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
subq $16, %rsp
movq %rdi, -8(%rbp)
movq -8(%rbp), %rax
movq (%rax), %rax
movq (%rax), %rdx
movq -8(%rbp), %rax
movq %rax, %rdi
call *%rdx
nop
leave
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE1734:
.size _Z15makeAnimalSoundRK6Animal, .-_Z15makeAnimalSoundRK6Animal
.globl main
.type main, @function
main:
.LFB1735:
.cfi_startproc
endbr64
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
subq $32, %rsp
movq %fs:40, %rax
movq %rax, -8(%rbp)
xorl %eax, %eax
leaq 16+_ZTV3Dog(%rip), %rax
movq %rax, -24(%rbp)
leaq 16+_ZTV3Cat(%rip), %rax
movq %rax, -16(%rbp)
leaq -24(%rbp), %rax
movq %rax, %rdi
call _Z15makeAnimalSoundRK6Animal
leaq -16(%rbp), %rax
movq %rax, %rdi
call _Z15makeAnimalSoundRK6Animal
movl $0, %eax
movq -8(%rbp), %rdx
subq %fs:40, %rdx
je .L6
call __stack_chk_fail@PLT
.L6:
leave
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE1735:
.size main, .-main
.weak _ZTV3Cat
.section .data.rel.ro.local._ZTV3Cat,"awG",@progbits,_ZTV3Cat,comdat
.align 8
.type _ZTV3Cat, @object
.size _ZTV3Cat, 24
_ZTV3Cat:
.quad 0
.quad _ZTI3Cat
.quad _ZNK3Cat9makeSoundEv
.weak _ZTV3Dog
.section .data.rel.ro.local._ZTV3Dog,"awG",@progbits,_ZTV3Dog,comdat
.align 8
.type _ZTV3Dog, @object
.size _ZTV3Dog, 24
_ZTV3Dog:
.quad 0
.quad _ZTI3Dog
.quad _ZNK3Dog9makeSoundEv
.weak _ZTI3Cat
.section .data.rel.ro._ZTI3Cat,"awG",@progbits,_ZTI3Cat,comdat
.align 8
.type _ZTI3Cat, @object
.size _ZTI3Cat, 24
_ZTI3Cat:
.quad _ZTVN10__cxxabiv120__si_class_type_infoE+16
.quad _ZTS3Cat
.quad _ZTI6Animal
.weak _ZTS3Cat
.section .rodata._ZTS3Cat,"aG",@progbits,_ZTS3Cat,comdat
.type _ZTS3Cat, @object
.size _ZTS3Cat, 5
_ZTS3Cat:
.string "3Cat"
.weak _ZTI3Dog
.section .data.rel.ro._ZTI3Dog,"awG",@progbits,_ZTI3Dog,comdat
.align 8
.type _ZTI3Dog, @object
.size _ZTI3Dog, 24
_ZTI3Dog:
.quad _ZTVN10__cxxabiv120__si_class_type_infoE+16
.quad _ZTS3Dog
.quad _ZTI6Animal
.weak _ZTS3Dog
.section .rodata._ZTS3Dog,"aG",@progbits,_ZTS3Dog,comdat
.type _ZTS3Dog, @object
.size _ZTS3Dog, 5
_ZTS3Dog:
.string "3Dog"
.weak _ZTI6Animal
.section .data.rel.ro._ZTI6Animal,"awG",@progbits,_ZTI6Animal,comdat
.align 8
.type _ZTI6Animal, @object
.size _ZTI6Animal, 16
_ZTI6Animal:
.quad _ZTVN10__cxxabiv117__class_type_infoE+16
.quad _ZTS6Animal
.weak _ZTS6Animal
.section .rodata._ZTS6Animal,"aG",@progbits,_ZTS6Animal,comdat
.align 8
.type _ZTS6Animal, @object
.size _ZTS6Animal, 8
_ZTS6Animal:
.string "6Animal"
.text
.type _Z41__static_initialization_and_destruction_0ii, @function
_Z41__static_initialization_and_destruction_0ii:
.LFB2247:
.cfi_startproc
endbr64
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
subq $16, %rsp
movl %edi, -4(%rbp)
movl %esi, -8(%rbp)
cmpl $1, -4(%rbp)
jne .L9
cmpl $65535, -8(%rbp)
jne .L9
leaq _ZStL8__ioinit(%rip), %rax
movq %rax, %rdi
call _ZNSt8ios_base4InitC1Ev@PLT
leaq __dso_handle(%rip), %rax
movq %rax, %rdx
leaq _ZStL8__ioinit(%rip), %rax
movq %rax, %rsi
movq _ZNSt8ios_base4InitD1Ev@GOTPCREL(%rip), %rax
movq %rax, %rdi
call __cxa_atexit@PLT
.L9:
nop
leave
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE2247:
.size _Z41__static_initialization_and_destruction_0ii, .-_Z41__static_initialization_and_destruction_0ii
.type _GLOBAL__sub_I__Z15makeAnimalSoundRK6Animal, @function
_GLOBAL__sub_I__Z15makeAnimalSoundRK6Animal:
.LFB2248:
.cfi_startproc
endbr64
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
movl $65535, %esi
movl $1, %edi
call _Z41__static_initialization_and_destruction_0ii
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE2248:
.size _GLOBAL__sub_I__Z15makeAnimalSoundRK6Animal, .-_GLOBAL__sub_I__Z15makeAnimalSoundRK6Animal
.section .init_array,"aw"
.align 8
.quad _GLOBAL__sub_I__Z15makeAnimalSoundRK6Animal
.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:
关键部分注释1:
# 字符串常量定义
.section .rodata
.LC0:
.string "Dog barks" # 存储字符串 "Dog barks" 用于 Dog 类的输出
# Dog 类的 makeSound 方法定义
.section .text._ZNK3Dog9makeSoundEv,"axG",@progbits,_ZNK3Dog9makeSoundEv,comdat
.align 2
.weak _ZNK3Dog9makeSoundEv
.type _ZNK3Dog9makeSoundEv, @function
_ZNK3Dog9makeSoundEv: # 函数开始
...
leaq .LC0(%rip), %rax # 加载 "Dog barks" 字符串的地址到 rax
...
call _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc@PLT # 调用输出操作符来打印字符串
...
ret # 函数返回
# 字符串常量定义
.section .rodata
.LC1:
.string "Cat meows" # 存储字符串 "Cat meows" 用于 Cat 类的输出
# Cat 类的 makeSound 方法定义
.section .text._ZNK3Cat9makeSoundEv,"axG",@progbits,_ZNK3Cat9makeSoundEv,comdat
.align 2
.weak _ZNK3Cat9makeSoundEv
.type _ZNK3Cat9makeSoundEv, @function
_ZNK3Cat9makeSoundEv: # 函数开始
...
leaq .LC1(%rip), %rax # 加载 "Cat meows" 字符串的地址到 rax
...
call _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc@PLT # 调用输出操作符来打印字符串
...
ret # 函数返回
# makeAnimalSound 函数定义
.text
.globl _Z15makeAnimalSoundRK6Animal
.type _Z15makeAnimalSoundRK6Animal, @function
_Z15makeAnimalSoundRK6Animal: # 函数开始
...
movq -8(%rbp), %rax # 从栈中加载 Animal 对象的引用
movq (%rax), %rax # 加载对象的虚函数表指针
movq (%rax), %rdx # 从虚函数表中获取 makeSound 方法的地址
movq -8(%rbp), %rax # 再次加载 Animal 对象的引用
movq %rax, %rdi # 将对象引用作为参数传递给 makeSound 方法
call *%rdx # 通过函数指针调用 makeSound 方法
...
ret # 函数返回
# main 函数定义
.text
.globl main
.type main, @function
main:
...
leaq -24(%rbp), %rax # 获取 Dog 对象的地址
movq %rax, %rdi # 将 Dog 对象的地址作为参数
call _Z15makeAnimalSoundRK6Animal # 调用 makeAnimalSound
...
leaq -16(%rbp), %rax # 获取 Cat 对象的地址
movq %rax, %rdi # 将 Cat 对象的地址作为参数
call _Z15makeAnimalSoundRK6Animal # 调用 makeAnimalSound
...
ret # 函数返回
?关键注释2:
.file "c.cpp" # 源文件指示
.text # 开始文本段,包含程序的代码
.local _ZStL8__ioinit # 声明局部变量(用于I/O库初始化)
.comm _ZStL8__ioinit,1,1 # 分配内存给局部变量
.section .rodata # 只读数据段
.LC0: # 字符串 "Dog barks" 的标签
.string "Dog barks" # 存储字符串 "Dog barks"
.section .text._ZNK3Dog9makeSoundEv,"axG",@progbits,_ZNK3Dog9makeSoundEv,comdat
.align 2
.weak _ZNK3Dog9makeSoundEv # Dog 类的 makeSound 方法的弱符号声明
.type _ZNK3Dog9makeSoundEv, @function # 指明 _ZNK3Dog9makeSoundEv 是一个函数
_ZNK3Dog9makeSoundEv: # Dog 类的 makeSound 方法
.LFB1732:
.cfi_startproc
endbr64 # 分支目标指令,用于安全
pushq %rbp # 保存基指针寄存器
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp # 更新栈基指针
.cfi_def_cfa_register 6
subq $16, %rsp # 为局部变量分配栈空间
movq %rdi, -8(%rbp) # 将 this 指针保存到栈上
leaq .LC0(%rip), %rax # 加载字符串 "Dog barks" 的地址
movq %rax, %rsi # 将字符串地址作为第二个参数
leaq _ZSt4cout(%rip), %rax # 加载 cout 对象的地址
movq %rax, %rdi # 将 cout 对象地址作为第一个参数
call _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc@PLT # 调用输出操作符函数
movq _ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_@GOTPCREL(%rip), %rdx # 加载 endl 对象的地址
movq %rdx, %rsi # 将 endl 对象地址作为第二个参数
movq %rax, %rdi # 将 cout 对象地址作为第一个参数
call _ZNSolsEPFRSoS_E@PLT # 调用输出操作符函数
nop # 无操作指令
leave # 恢复栈和基指针
.cfi_def_cfa 7, 8
ret # 返回
.cfi_endproc
.LFE1732:
.size _ZNK3Dog9makeSoundEv, .-_ZNK3Dog9makeSoundEv # 指定函数大小
.section .rodata
.LC1: # 字符串 "Cat meows" 的标签
.string "Cat meows" # 存储字符串 "Cat meows"
- 字符串常量:为
Dog::makeSound
和Cat::makeSound
方法中的输出字符串定义了常量。 Dog::makeSound
和Cat::makeSound
方法:这两个方法的实现在汇编中类似,主要区别在于加载的字符串常量不同。这些方法使用标准输出流std::cout
打印各自的字符串。makeAnimalSound
函数:这个函数展示了多态的实现。它首先从传入的Animal
类型对象中获取虚函数表指针,然后从虚函数表中获取makeSound
方法的地址,并通过这个地址调用方法。main
函数:在main
函数中,创建Dog
和Cat
类型的对象,并通过makeAnimalSound
函数演示多态的行为。
案例四 模板类
#include <iostream>
template<typename T>
class Container {
private:
T value;
public:
Container(T v) : value(v) {}
void show() const {
std::cout << value << std::endl;
}
T getValue() const { return value; }
};
template<typename T>
void printTwice(const Container<T>& container) {
std::cout << container.getValue() << " " << container.getValue() << std::endl;
}
int main() {
Container<int> intContainer(42);
Container<std::string> stringContainer("Hello, world!");
intContainer.show();
stringContainer.show();
printTwice(intContainer);
printTwice(stringContainer);
return 0;
}
伪代码汇编:
# Container<int> 类的构造函数
Container_int_Constructor:
push rbp
mov rbp, rsp
mov [rbp-4], rdi # 第一个参数 this 指针
mov [rbp-8], esi # 第二个参数 value
mov rax, [rbp-4]
mov edx, [rbp-8]
mov [rax], edx # 将 value 赋值给 this->value
pop rbp
ret
# Container<int> 的 show 方法
Container_int_show:
push rbp
mov rbp, rsp
mov rax, [rbp-16] # 加载 this 指针
mov ecx, [rax] # 加载 this->value
; 调用标准库输出打印 ecx
pop rbp
ret
# Container<std::string> 类的构造函数
Container_string_Constructor:
; 类似于 Container<int>,但处理字符串对象
# Container<std::string> 的 show 方法
Container_string_show:
; 类似于 Container<int> 的 show 方法,但打印字符串
# printTwice 模板函数
template_printTwice:
push rbp
mov rbp, rsp
mov rax, [rbp-16] # 加载 Container 对象的引用
; 调用 Container::getValue 方法
; 调用标准库输出打印返回值两次
pop rbp
ret
# main 函数
main:
push rbp
mov rbp, rsp
; 创建 Container<int> 对象
sub rsp, 24 # 为 Container<int> 分配空间
mov esi, 42 # 整数值
lea rdi, [rsp+16] # 对象地址
call Container_int_Constructor
; 创建 Container<std::string> 对象
sub rsp, 48 # 为 Container<std::string> 分配空间
; 初始化字符串
lea rdi, [rsp] # 对象地址
call Container_string_Constructor
; 调用 show 方法
lea rdi, [rsp+16] # Container<int> 对象地址
call Container_int_show
lea rdi, [rsp] # Container<std::string> 对象地址
call Container_string_show
; 调用 printTwice 函数
lea rdi, [rsp+16] # Container<int> 对象地址
call template_printTwice
lea rdi, [rsp] # Container<std::string> 对象地址
call template_printTwice
leave
ret
关键点解释:
- 模板实例化:针对不同类型(如
int
和std::string
),编译器生成了不同的函数实例。这些函数实例在汇编代码中具有相似的结构,但处理的数据类型不同。 - 构造函数:每个模板实例的构造函数初始化其成员变量。
- 成员函数:
show
方法根据不同的模板实例进行了实现,以适应不同类型的成员变量。 - 模板函数调用:
printTwice
函数显示了泛型函数的调用方式,根据传入对象的类型处理输出。
发现的问题解析
????????当C++模板类或函数需要为许多不同的类型实例化时,编译器会为每个使用到的具体类型生成一个单独的实例。这是模板实例化的基本机制。以下是处理多种类型实例化时的几个要点和策略:
1. 编译器的自动实例化
????????对于模板类或函数,编译器会在编译时自动为每个使用到的类型生成一个实例。例如,如果你的代码中使用了 Container<int>
、Container<double>
和 Container<std::string>
,编译器将为这三种类型生成三个不同的实例。
2. 显式实例化
????????在某些情况下,你可能想要显式地指定模板的实例化,尤其是当模板的定义和使用分布在不同的源文件中时。你可以在一个源文件中定义模板,然后在另一个源文件中显式实例化它,如下所示:
template class Container<int>; // 显式实例化 Container<int>
template class Container<double>; // 显式实例化 Container<double>
3. 代码膨胀问题
????????模板的一个潜在问题是代码膨胀(Code Bloat)。由于每个类型的实例化都会生成新的代码,大量使用模板可能会导致编译后的程序体积显著增加。为了管理这一点,可以:
- 避免不必要的实例化:只为确实需要的类型实例化模板。
- 使用非模板函数或类成员:对于不依赖于模板参数的功能,可以使用普通的非模板成员函数或类成员。
4. 编译时间增加
????????大量的模板实例化也可能导致编译时间显著增加。合理组织代码,减少不必要的头文件包含,可以在一定程度上缓解这个问题。
5. 类型特征和条件编译
????????在某些情况下,你可能希望根据类型的特征来定制模板的行为。C++11及以后版本提供了丰富的类型特征(type traits)和 constexpr if
来支持在编译时根据类型进行条件编译。
6. 模板元编程
????????对于复杂的类型操作和编译时计算,模板元编程(Template Metaprogramming)可以发挥强大的作用。这是一种利用模板实现编译时计算和逻辑判断的技术。
? ? ? ? 这是个复杂的技术,以后再说。
实际汇编
.file "c.cpp"
.text
.section .text._ZNSt11char_traitsIcE6lengthEPKc,"axG",@progbits,_ZNSt11char_traitsIcE6lengthEPKc,comdat
.weak _ZNSt11char_traitsIcE6lengthEPKc
.type _ZNSt11char_traitsIcE6lengthEPKc, @function
_ZNSt11char_traitsIcE6lengthEPKc:
.LFB450:
.cfi_startproc
endbr64
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
subq $32, %rsp
movq %rdi, -24(%rbp)
movq -24(%rbp), %rax
movq %rax, -8(%rbp)
movl $0, %eax
testb %al, %al
je .L3
movq -24(%rbp), %rax
movq %rax, %rdi
call _ZN9__gnu_cxx11char_traitsIcE6lengthEPKc
jmp .L4
.L3:
movq -24(%rbp), %rax
movq %rax, %rdi
call strlen@PLT
nop
.L4:
leave
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE450:
.size _ZNSt11char_traitsIcE6lengthEPKc, .-_ZNSt11char_traitsIcE6lengthEPKc
.local _ZStL8__ioinit
.comm _ZStL8__ioinit,1,1
.section .text._ZN9ContainerINSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEED2Ev,"axG",@progbits,_ZN9ContainerINSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEED5Ev,comdat
.align 2
.weak _ZN9ContainerINSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEED2Ev
.type _ZN9ContainerINSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEED2Ev, @function
_ZN9ContainerINSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEED2Ev:
.LFB1737:
.cfi_startproc
endbr64
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
subq $16, %rsp
movq %rdi, -8(%rbp)
movq -8(%rbp), %rax
movq %rax, %rdi
call _ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEED1Ev@PLT
nop
leave
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE1737:
.size _ZN9ContainerINSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEED2Ev, .-_ZN9ContainerINSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEED2Ev
.weak _ZN9ContainerINSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEED1Ev
.set _ZN9ContainerINSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEED1Ev,_ZN9ContainerINSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEED2Ev
.section .rodata
.LC0:
.string "Hello, world!"
.text
.globl main
.type main, @function
main:
.LFB1735:
.cfi_startproc
.cfi_personality 0x9b,DW.ref.__gxx_personality_v0
.cfi_lsda 0x1b,.LLSDA1735
endbr64
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
pushq %rbx
subq $104, %rsp
.cfi_offset 3, -24
movq %fs:40, %rax
movq %rax, -24(%rbp)
xorl %eax, %eax
leaq -100(%rbp), %rax
movl $42, %esi
movq %rax, %rdi
call _ZN9ContainerIiEC1Ei
leaq -101(%rbp), %rax
movq %rax, %rdi
call _ZNSaIcEC1Ev@PLT
leaq -101(%rbp), %rdx
leaq -64(%rbp), %rax
leaq .LC0(%rip), %rcx
movq %rcx, %rsi
movq %rax, %rdi
.LEHB0:
call _ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEC1IS3_EEPKcRKS3_
.LEHE0:
leaq -64(%rbp), %rdx
leaq -96(%rbp), %rax
movq %rdx, %rsi
movq %rax, %rdi
.LEHB1:
call _ZN9ContainerINSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEEC1ES5_
.LEHE1:
leaq -64(%rbp), %rax
movq %rax, %rdi
call _ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEED1Ev@PLT
leaq -101(%rbp), %rax
movq %rax, %rdi
call _ZNSaIcED1Ev@PLT
leaq -100(%rbp), %rax
movq %rax, %rdi
.LEHB2:
call _ZNK9ContainerIiE4showEv
leaq -96(%rbp), %rax
movq %rax, %rdi
call _ZNK9ContainerINSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEE4showEv
leaq -100(%rbp), %rax
movq %rax, %rdi
call _Z10printTwiceIiEvRK9ContainerIT_E
leaq -96(%rbp), %rax
movq %rax, %rdi
call _Z10printTwiceINSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEEvRK9ContainerIT_E
.LEHE2:
movl $0, %ebx
leaq -96(%rbp), %rax
movq %rax, %rdi
call _ZN9ContainerINSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEED1Ev
movl %ebx, %eax
movq -24(%rbp), %rdx
subq %fs:40, %rdx
je .L11
jmp .L15
.L13:
endbr64
movq %rax, %rbx
leaq -64(%rbp), %rax
movq %rax, %rdi
call _ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEED1Ev@PLT
jmp .L9
.L12:
endbr64
movq %rax, %rbx
.L9:
leaq -101(%rbp), %rax
movq %rax, %rdi
call _ZNSaIcED1Ev@PLT
movq %rbx, %rax
movq %rax, %rdi
.LEHB3:
call _Unwind_Resume@PLT
.L14:
endbr64
movq %rax, %rbx
leaq -96(%rbp), %rax
movq %rax, %rdi
call _ZN9ContainerINSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEED1Ev
movq %rbx, %rax
movq %rax, %rdi
call _Unwind_Resume@PLT
.LEHE3:
.L15:
call __stack_chk_fail@PLT
.L11:
movq -8(%rbp), %rbx
leave
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE1735:
.globl __gxx_personality_v0
.section .gcc_except_table,"a",@progbits
.LLSDA1735:
.byte 0xff
.byte 0xff
.byte 0x1
.uleb128 .LLSDACSE1735-.LLSDACSB1735
.LLSDACSB1735:
.uleb128 .LEHB0-.LFB1735
.uleb128 .LEHE0-.LEHB0
.uleb128 .L12-.LFB1735
.uleb128 0
.uleb128 .LEHB1-.LFB1735
.uleb128 .LEHE1-.LEHB1
.uleb128 .L13-.LFB1735
.uleb128 0
.uleb128 .LEHB2-.LFB1735
.uleb128 .LEHE2-.LEHB2
.uleb128 .L14-.LFB1735
.uleb128 0
.uleb128 .LEHB3-.LFB1735
.uleb128 .LEHE3-.LEHB3
.uleb128 0
.uleb128 0
.LLSDACSE1735:
.text
.size main, .-main
.section .text._ZN9__gnu_cxx11char_traitsIcE2eqERKcS3_,"axG",@progbits,_ZN9__gnu_cxx11char_traitsIcE2eqERKcS3_,comdat
.weak _ZN9__gnu_cxx11char_traitsIcE2eqERKcS3_
.type _ZN9__gnu_cxx11char_traitsIcE2eqERKcS3_, @function
_ZN9__gnu_cxx11char_traitsIcE2eqERKcS3_:
.LFB1740:
.cfi_startproc
endbr64
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
movq %rdi, -8(%rbp)
movq %rsi, -16(%rbp)
movq -8(%rbp), %rax
movzbl (%rax), %edx
movq -16(%rbp), %rax
movzbl (%rax), %eax
cmpb %al, %dl
sete %al
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE1740:
.size _ZN9__gnu_cxx11char_traitsIcE2eqERKcS3_, .-_ZN9__gnu_cxx11char_traitsIcE2eqERKcS3_
.section .text._ZN9__gnu_cxx11char_traitsIcE6lengthEPKc,"axG",@progbits,_ZN9__gnu_cxx11char_traitsIcE6lengthEPKc,comdat
.align 2
.weak _ZN9__gnu_cxx11char_traitsIcE6lengthEPKc
.type _ZN9__gnu_cxx11char_traitsIcE6lengthEPKc, @function
_ZN9__gnu_cxx11char_traitsIcE6lengthEPKc:
.LFB1739:
.cfi_startproc
endbr64
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
subq $48, %rsp
movq %rdi, -40(%rbp)
movq %fs:40, %rax
movq %rax, -8(%rbp)
xorl %eax, %eax
movq $0, -16(%rbp)
jmp .L19
.L20:
addq $1, -16(%rbp)
.L19:
movb $0, -17(%rbp)
movq -40(%rbp), %rdx
movq -16(%rbp), %rax
addq %rax, %rdx
leaq -17(%rbp), %rax
movq %rax, %rsi
movq %rdx, %rdi
call _ZN9__gnu_cxx11char_traitsIcE2eqERKcS3_
xorl $1, %eax
testb %al, %al
jne .L20
movq -16(%rbp), %rax
movq -8(%rbp), %rdx
subq %fs:40, %rdx
je .L22
call __stack_chk_fail@PLT
.L22:
leave
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE1739:
.size _ZN9__gnu_cxx11char_traitsIcE6lengthEPKc, .-_ZN9__gnu_cxx11char_traitsIcE6lengthEPKc
.section .text._ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE12_Alloc_hiderD2Ev,"axG",@progbits,_ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE12_Alloc_hiderD5Ev,comdat
.align 2
.weak _ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE12_Alloc_hiderD2Ev
.type _ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE12_Alloc_hiderD2Ev, @function
_ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE12_Alloc_hiderD2Ev:
.LFB1847:
.cfi_startproc
endbr64
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
subq $16, %rsp
movq %rdi, -8(%rbp)
movq -8(%rbp), %rax
movq %rax, %rdi
call _ZNSaIcED2Ev@PLT
nop
leave
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE1847:
.size _ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE12_Alloc_hiderD2Ev, .-_ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE12_Alloc_hiderD2Ev
.weak _ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE12_Alloc_hiderD1Ev
.set _ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE12_Alloc_hiderD1Ev,_ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE12_Alloc_hiderD2Ev
.section .text._ZN9ContainerIiEC2Ei,"axG",@progbits,_ZN9ContainerIiEC5Ei,comdat
.align 2
.weak _ZN9ContainerIiEC2Ei
.type _ZN9ContainerIiEC2Ei, @function
_ZN9ContainerIiEC2Ei:
.LFB2000:
.cfi_startproc
endbr64
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
movq %rdi, -8(%rbp)
movl %esi, -12(%rbp)
movq -8(%rbp), %rax
movl -12(%rbp), %edx
movl %edx, (%rax)
nop
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE2000:
.size _ZN9ContainerIiEC2Ei, .-_ZN9ContainerIiEC2Ei
.weak _ZN9ContainerIiEC1Ei
.set _ZN9ContainerIiEC1Ei,_ZN9ContainerIiEC2Ei
.section .text._ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEC2IS3_EEPKcRKS3_,"axG",@progbits,_ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEC5IS3_EEPKcRKS3_,comdat
.align 2
.weak _ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEC2IS3_EEPKcRKS3_
.type _ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEC2IS3_EEPKcRKS3_, @function
_ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEC2IS3_EEPKcRKS3_:
.LFB2003:
.cfi_startproc
.cfi_personality 0x9b,DW.ref.__gxx_personality_v0
.cfi_lsda 0x1b,.LLSDA2003
endbr64
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
pushq %rbx
subq $72, %rsp
.cfi_offset 3, -24
movq %rdi, -56(%rbp)
movq %rsi, -64(%rbp)
movq %rdx, -72(%rbp)
movq %fs:40, %rax
movq %rax, -24(%rbp)
xorl %eax, %eax
movq -56(%rbp), %rbx
movq -56(%rbp), %rax
movq %rax, %rdi
.LEHB4:
call _ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE13_M_local_dataEv@PLT
movq %rax, %rcx
movq -72(%rbp), %rax
movq %rax, %rdx
movq %rcx, %rsi
movq %rbx, %rdi
call _ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE12_Alloc_hiderC1EPcRKS3_@PLT
.LEHE4:
cmpq $0, -64(%rbp)
je .L26
movq -64(%rbp), %rax
movq %rax, %rdi
.LEHB5:
call _ZNSt11char_traitsIcE6lengthEPKc
movq -64(%rbp), %rdx
addq %rdx, %rax
jmp .L27
.L26:
movl $1, %eax
.L27:
movq %rax, -32(%rbp)
movq -32(%rbp), %rdx
movq -64(%rbp), %rcx
movq -56(%rbp), %rax
movq %rcx, %rsi
movq %rax, %rdi
call _ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE12_M_constructIPKcEEvT_S8_St20forward_iterator_tag
.LEHE5:
jmp .L31
.L30:
endbr64
movq %rax, %rbx
movq -56(%rbp), %rax
movq %rax, %rdi
call _ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE12_Alloc_hiderD1Ev
movq %rbx, %rax
movq %rax, %rdi
.LEHB6:
call _Unwind_Resume@PLT
.LEHE6:
.L31:
movq -24(%rbp), %rax
subq %fs:40, %rax
je .L29
call __stack_chk_fail@PLT
.L29:
movq -8(%rbp), %rbx
leave
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE2003:
.section .gcc_except_table
.LLSDA2003:
.byte 0xff
.byte 0xff
.byte 0x1
.uleb128 .LLSDACSE2003-.LLSDACSB2003
.LLSDACSB2003:
.uleb128 .LEHB4-.LFB2003
.uleb128 .LEHE4-.LEHB4
.uleb128 0
.uleb128 0
.uleb128 .LEHB5-.LFB2003
.uleb128 .LEHE5-.LEHB5
.uleb128 .L30-.LFB2003
.uleb128 0
.uleb128 .LEHB6-.LFB2003
.uleb128 .LEHE6-.LEHB6
.uleb128 0
.uleb128 0
.LLSDACSE2003:
.section .text._ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEC2IS3_EEPKcRKS3_,"axG",@progbits,_ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEC5IS3_EEPKcRKS3_,comdat
.size _ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEC2IS3_EEPKcRKS3_, .-_ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEC2IS3_EEPKcRKS3_
.weak _ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEC1IS3_EEPKcRKS3_
.set _ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEC1IS3_EEPKcRKS3_,_ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEC2IS3_EEPKcRKS3_
.section .text._ZN9ContainerINSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEEC2ES5_,"axG",@progbits,_ZN9ContainerINSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEEC5ES5_,comdat
.align 2
.weak _ZN9ContainerINSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEEC2ES5_
.type _ZN9ContainerINSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEEC2ES5_, @function
_ZN9ContainerINSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEEC2ES5_:
.LFB2006:
.cfi_startproc
endbr64
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
subq $16, %rsp
movq %rdi, -8(%rbp)
movq %rsi, -16(%rbp)
movq -8(%rbp), %rax
movq -16(%rbp), %rdx
movq %rdx, %rsi
movq %rax, %rdi
call _ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEC1ERKS4_@PLT
nop
leave
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE2006:
.size _ZN9ContainerINSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEEC2ES5_, .-_ZN9ContainerINSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEEC2ES5_
.weak _ZN9ContainerINSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEEC1ES5_
.set _ZN9ContainerINSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEEC1ES5_,_ZN9ContainerINSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEEC2ES5_
.section .text._ZNK9ContainerIiE4showEv,"axG",@progbits,_ZNK9ContainerIiE4showEv,comdat
.align 2
.weak _ZNK9ContainerIiE4showEv
.type _ZNK9ContainerIiE4showEv, @function
_ZNK9ContainerIiE4showEv:
.LFB2008:
.cfi_startproc
endbr64
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
subq $16, %rsp
movq %rdi, -8(%rbp)
movq -8(%rbp), %rax
movl (%rax), %eax
movl %eax, %esi
leaq _ZSt4cout(%rip), %rax
movq %rax, %rdi
call _ZNSolsEi@PLT
movq _ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_@GOTPCREL(%rip), %rdx
movq %rdx, %rsi
movq %rax, %rdi
call _ZNSolsEPFRSoS_E@PLT
nop
leave
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE2008:
.size _ZNK9ContainerIiE4showEv, .-_ZNK9ContainerIiE4showEv
.section .text._ZNK9ContainerINSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEE4showEv,"axG",@progbits,_ZNK9ContainerINSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEE4showEv,comdat
.align 2
.weak _ZNK9ContainerINSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEE4showEv
.type _ZNK9ContainerINSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEE4showEv, @function
_ZNK9ContainerINSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEE4showEv:
.LFB2009:
.cfi_startproc
endbr64
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
subq $16, %rsp
movq %rdi, -8(%rbp)
movq -8(%rbp), %rax
movq %rax, %rsi
leaq _ZSt4cout(%rip), %rax
movq %rax, %rdi
call _ZStlsIcSt11char_traitsIcESaIcEERSt13basic_ostreamIT_T0_ES7_RKNSt7__cxx1112basic_stringIS4_S5_T1_EE@PLT
movq _ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_@GOTPCREL(%rip), %rdx
movq %rdx, %rsi
movq %rax, %rdi
call _ZNSolsEPFRSoS_E@PLT
nop
leave
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE2009:
.size _ZNK9ContainerINSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEE4showEv, .-_ZNK9ContainerINSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEE4showEv
.section .rodata
.LC1:
.string " "
.section .text._Z10printTwiceIiEvRK9ContainerIT_E,"axG",@progbits,_Z10printTwiceIiEvRK9ContainerIT_E,comdat
.weak _Z10printTwiceIiEvRK9ContainerIT_E
.type _Z10printTwiceIiEvRK9ContainerIT_E, @function
_Z10printTwiceIiEvRK9ContainerIT_E:
.LFB2010:
.cfi_startproc
endbr64
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
pushq %rbx
subq $24, %rsp
.cfi_offset 3, -24
movq %rdi, -24(%rbp)
movq -24(%rbp), %rax
movq %rax, %rdi
call _ZNK9ContainerIiE8getValueEv
movl %eax, %esi
leaq _ZSt4cout(%rip), %rax
movq %rax, %rdi
call _ZNSolsEi@PLT
movq %rax, %rdx
leaq .LC1(%rip), %rax
movq %rax, %rsi
movq %rdx, %rdi
call _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc@PLT
movq %rax, %rbx
movq -24(%rbp), %rax
movq %rax, %rdi
call _ZNK9ContainerIiE8getValueEv
movl %eax, %esi
movq %rbx, %rdi
call _ZNSolsEi@PLT
movq _ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_@GOTPCREL(%rip), %rdx
movq %rdx, %rsi
movq %rax, %rdi
call _ZNSolsEPFRSoS_E@PLT
nop
movq -8(%rbp), %rbx
leave
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE2010:
.size _Z10printTwiceIiEvRK9ContainerIT_E, .-_Z10printTwiceIiEvRK9ContainerIT_E
.section .text._Z10printTwiceINSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEEvRK9ContainerIT_E,"axG",@progbits,_Z10printTwiceINSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEEvRK9ContainerIT_E,comdat
.weak _Z10printTwiceINSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEEvRK9ContainerIT_E
.type _Z10printTwiceINSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEEvRK9ContainerIT_E, @function
_Z10printTwiceINSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEEvRK9ContainerIT_E:
.LFB2011:
.cfi_startproc
.cfi_personality 0x9b,DW.ref.__gxx_personality_v0
.cfi_lsda 0x1b,.LLSDA2011
endbr64
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
pushq %rbx
subq $104, %rsp
.cfi_offset 3, -24
movq %rdi, -104(%rbp)
movq %fs:40, %rax
movq %rax, -24(%rbp)
xorl %eax, %eax
leaq -96(%rbp), %rax
movq -104(%rbp), %rdx
movq %rdx, %rsi
movq %rax, %rdi
.LEHB7:
call _ZNK9ContainerINSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEE8getValueEv
.LEHE7:
leaq -96(%rbp), %rax
movq %rax, %rsi
leaq _ZSt4cout(%rip), %rax
movq %rax, %rdi
.LEHB8:
call _ZStlsIcSt11char_traitsIcESaIcEERSt13basic_ostreamIT_T0_ES7_RKNSt7__cxx1112basic_stringIS4_S5_T1_EE@PLT
movq %rax, %rdx
leaq .LC1(%rip), %rax
movq %rax, %rsi
movq %rdx, %rdi
call _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc@PLT
movq %rax, %rbx
leaq -64(%rbp), %rax
movq -104(%rbp), %rdx
movq %rdx, %rsi
movq %rax, %rdi
call _ZNK9ContainerINSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEE8getValueEv
.LEHE8:
leaq -64(%rbp), %rax
movq %rax, %rsi
movq %rbx, %rdi
.LEHB9:
call _ZStlsIcSt11char_traitsIcESaIcEERSt13basic_ostreamIT_T0_ES7_RKNSt7__cxx1112basic_stringIS4_S5_T1_EE@PLT
movq _ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_@GOTPCREL(%rip), %rdx
movq %rdx, %rsi
movq %rax, %rdi
call _ZNSolsEPFRSoS_E@PLT
.LEHE9:
leaq -64(%rbp), %rax
movq %rax, %rdi
call _ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEED1Ev@PLT
leaq -96(%rbp), %rax
movq %rax, %rdi
call _ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEED1Ev@PLT
nop
movq -24(%rbp), %rax
subq %fs:40, %rax
je .L39
jmp .L42
.L41:
endbr64
movq %rax, %rbx
leaq -64(%rbp), %rax
movq %rax, %rdi
call _ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEED1Ev@PLT
jmp .L38
.L40:
endbr64
movq %rax, %rbx
.L38:
leaq -96(%rbp), %rax
movq %rax, %rdi
call _ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEED1Ev@PLT
movq %rbx, %rax
movq %rax, %rdi
.LEHB10:
call _Unwind_Resume@PLT
.LEHE10:
.L42:
call __stack_chk_fail@PLT
.L39:
movq -8(%rbp), %rbx
leave
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE2011:
.section .gcc_except_table
.LLSDA2011:
.byte 0xff
.byte 0xff
.byte 0x1
.uleb128 .LLSDACSE2011-.LLSDACSB2011
.LLSDACSB2011:
.uleb128 .LEHB7-.LFB2011
.uleb128 .LEHE7-.LEHB7
.uleb128 0
.uleb128 0
.uleb128 .LEHB8-.LFB2011
.uleb128 .LEHE8-.LEHB8
.uleb128 .L40-.LFB2011
.uleb128 0
.uleb128 .LEHB9-.LFB2011
.uleb128 .LEHE9-.LEHB9
.uleb128 .L41-.LFB2011
.uleb128 0
.uleb128 .LEHB10-.LFB2011
.uleb128 .LEHE10-.LEHB10
.uleb128 0
.uleb128 0
.LLSDACSE2011:
.section .text._Z10printTwiceINSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEEvRK9ContainerIT_E,"axG",@progbits,_Z10printTwiceINSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEEvRK9ContainerIT_E,comdat
.size _Z10printTwiceINSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEEvRK9ContainerIT_E, .-_Z10printTwiceINSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEEvRK9ContainerIT_E
.section .text._ZSt8distanceIPKcENSt15iterator_traitsIT_E15difference_typeES3_S3_,"axG",@progbits,_ZSt8distanceIPKcENSt15iterator_traitsIT_E15difference_typeES3_S3_,comdat
.weak _ZSt8distanceIPKcENSt15iterator_traitsIT_E15difference_typeES3_S3_
.type _ZSt8distanceIPKcENSt15iterator_traitsIT_E15difference_typeES3_S3_, @function
_ZSt8distanceIPKcENSt15iterator_traitsIT_E15difference_typeES3_S3_:
.LFB2090:
.cfi_startproc
endbr64
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
subq $16, %rsp
movq %rdi, -8(%rbp)
movq %rsi, -16(%rbp)
leaq -8(%rbp), %rax
movq %rax, %rdi
call _ZSt19__iterator_categoryIPKcENSt15iterator_traitsIT_E17iterator_categoryERKS3_
movq -8(%rbp), %rax
movq -16(%rbp), %rdx
movq %rdx, %rsi
movq %rax, %rdi
call _ZSt10__distanceIPKcENSt15iterator_traitsIT_E15difference_typeES3_S3_St26random_access_iterator_tag
leave
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE2090:
.size _ZSt8distanceIPKcENSt15iterator_traitsIT_E15difference_typeES3_S3_, .-_ZSt8distanceIPKcENSt15iterator_traitsIT_E15difference_typeES3_S3_
.section .rodata
.align 8
.LC2:
.string "basic_string::_M_construct null not valid"
.section .text._ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE12_M_constructIPKcEEvT_S8_St20forward_iterator_tag,"axG",@progbits,_ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE12_M_constructIPKcEEvT_S8_St20forward_iterator_tag,comdat
.align 2
.weak _ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE12_M_constructIPKcEEvT_S8_St20forward_iterator_tag
.type _ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE12_M_constructIPKcEEvT_S8_St20forward_iterator_tag, @function
_ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE12_M_constructIPKcEEvT_S8_St20forward_iterator_tag:
.LFB2089:
.cfi_startproc
.cfi_personality 0x9b,DW.ref.__gxx_personality_v0
.cfi_lsda 0x1b,.LLSDA2089
endbr64
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
pushq %rbx
subq $56, %rsp
.cfi_offset 3, -24
movq %rdi, -40(%rbp)
movq %rsi, -48(%rbp)
movq %rdx, -56(%rbp)
movq %fs:40, %rax
movq %rax, -24(%rbp)
xorl %eax, %eax
movq -48(%rbp), %rax
movq %rax, %rdi
call _ZN9__gnu_cxx17__is_null_pointerIKcEEbPT_
testb %al, %al
je .L46
movq -48(%rbp), %rax
cmpq -56(%rbp), %rax
je .L46
movl $1, %eax
jmp .L47
.L46:
movl $0, %eax
.L47:
testb %al, %al
je .L48
leaq .LC2(%rip), %rax
movq %rax, %rdi
.LEHB11:
call _ZSt19__throw_logic_errorPKc@PLT
.L48:
movq -56(%rbp), %rdx
movq -48(%rbp), %rax
movq %rdx, %rsi
movq %rax, %rdi
call _ZSt8distanceIPKcENSt15iterator_traitsIT_E15difference_typeES3_S3_
movq %rax, -32(%rbp)
movq -32(%rbp), %rax
cmpq $15, %rax
jbe .L49
leaq -32(%rbp), %rcx
movq -40(%rbp), %rax
movl $0, %edx
movq %rcx, %rsi
movq %rax, %rdi
call _ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE9_M_createERmm@PLT
movq %rax, %rdx
movq -40(%rbp), %rax
movq %rdx, %rsi
movq %rax, %rdi
call _ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE7_M_dataEPc@PLT
movq -32(%rbp), %rdx
movq -40(%rbp), %rax
movq %rdx, %rsi
movq %rax, %rdi
call _ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE11_M_capacityEm@PLT
.LEHE11:
.L49:
movq -40(%rbp), %rax
movq %rax, %rdi
.LEHB12:
call _ZNKSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE7_M_dataEv@PLT
.LEHE12:
movq %rax, %rcx
movq -56(%rbp), %rdx
movq -48(%rbp), %rax
movq %rax, %rsi
movq %rcx, %rdi
call _ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE13_S_copy_charsEPcPKcS7_@PLT
movq -32(%rbp), %rdx
movq -40(%rbp), %rax
movq %rdx, %rsi
movq %rax, %rdi
.LEHB13:
call _ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE13_M_set_lengthEm@PLT
.LEHE13:
nop
movq -24(%rbp), %rax
subq %fs:40, %rax
je .L52
jmp .L55
.L53:
endbr64
movq %rax, %rdi
call __cxa_begin_catch@PLT
movq -40(%rbp), %rax
movq %rax, %rdi
.LEHB14:
call _ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE10_M_disposeEv@PLT
call __cxa_rethrow@PLT
.LEHE14:
.L54:
endbr64
movq %rax, %rbx
call __cxa_end_catch@PLT
movq %rbx, %rax
movq %rax, %rdi
.LEHB15:
call _Unwind_Resume@PLT
.LEHE15:
.L55:
call __stack_chk_fail@PLT
.L52:
movq -8(%rbp), %rbx
leave
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE2089:
.section .gcc_except_table
.align 4
.LLSDA2089:
.byte 0xff
.byte 0x9b
.uleb128 .LLSDATT2089-.LLSDATTD2089
.LLSDATTD2089:
.byte 0x1
.uleb128 .LLSDACSE2089-.LLSDACSB2089
.LLSDACSB2089:
.uleb128 .LEHB11-.LFB2089
.uleb128 .LEHE11-.LEHB11
.uleb128 0
.uleb128 0
.uleb128 .LEHB12-.LFB2089
.uleb128 .LEHE12-.LEHB12
.uleb128 .L53-.LFB2089
.uleb128 0x1
.uleb128 .LEHB13-.LFB2089
.uleb128 .LEHE13-.LEHB13
.uleb128 0
.uleb128 0
.uleb128 .LEHB14-.LFB2089
.uleb128 .LEHE14-.LEHB14
.uleb128 .L54-.LFB2089
.uleb128 0
.uleb128 .LEHB15-.LFB2089
.uleb128 .LEHE15-.LEHB15
.uleb128 0
.uleb128 0
.LLSDACSE2089:
.byte 0x1
.byte 0
.align 4
.long 0
.LLSDATT2089:
.section .text._ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE12_M_constructIPKcEEvT_S8_St20forward_iterator_tag,"axG",@progbits,_ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE12_M_constructIPKcEEvT_S8_St20forward_iterator_tag,comdat
.size _ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE12_M_constructIPKcEEvT_S8_St20forward_iterator_tag, .-_ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE12_M_constructIPKcEEvT_S8_St20forward_iterator_tag
.section .text._ZNK9ContainerIiE8getValueEv,"axG",@progbits,_ZNK9ContainerIiE8getValueEv,comdat
.align 2
.weak _ZNK9ContainerIiE8getValueEv
.type _ZNK9ContainerIiE8getValueEv, @function
_ZNK9ContainerIiE8getValueEv:
.LFB2094:
.cfi_startproc
endbr64
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
movq %rdi, -8(%rbp)
movq -8(%rbp), %rax
movl (%rax), %eax
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE2094:
.size _ZNK9ContainerIiE8getValueEv, .-_ZNK9ContainerIiE8getValueEv
.section .text._ZNK9ContainerINSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEE8getValueEv,"axG",@progbits,_ZNK9ContainerINSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEE8getValueEv,comdat
.align 2
.weak _ZNK9ContainerINSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEE8getValueEv
.type _ZNK9ContainerINSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEE8getValueEv, @function
_ZNK9ContainerINSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEE8getValueEv:
.LFB2096:
.cfi_startproc
endbr64
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
subq $16, %rsp
movq %rdi, -8(%rbp)
movq %rsi, -16(%rbp)
movq -16(%rbp), %rdx
movq -8(%rbp), %rax
movq %rdx, %rsi
movq %rax, %rdi
call _ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEC1ERKS4_@PLT
movq -8(%rbp), %rax
leave
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE2096:
.size _ZNK9ContainerINSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEE8getValueEv, .-_ZNK9ContainerINSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEE8getValueEv
.section .text._ZN9__gnu_cxx17__is_null_pointerIKcEEbPT_,"axG",@progbits,_ZN9__gnu_cxx17__is_null_pointerIKcEEbPT_,comdat
.weak _ZN9__gnu_cxx17__is_null_pointerIKcEEbPT_
.type _ZN9__gnu_cxx17__is_null_pointerIKcEEbPT_, @function
_ZN9__gnu_cxx17__is_null_pointerIKcEEbPT_:
.LFB2131:
.cfi_startproc
endbr64
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
movq %rdi, -8(%rbp)
cmpq $0, -8(%rbp)
sete %al
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE2131:
.size _ZN9__gnu_cxx17__is_null_pointerIKcEEbPT_, .-_ZN9__gnu_cxx17__is_null_pointerIKcEEbPT_
.section .text._ZSt19__iterator_categoryIPKcENSt15iterator_traitsIT_E17iterator_categoryERKS3_,"axG",@progbits,_ZSt19__iterator_categoryIPKcENSt15iterator_traitsIT_E17iterator_categoryERKS3_,comdat
.weak _ZSt19__iterator_categoryIPKcENSt15iterator_traitsIT_E17iterator_categoryERKS3_
.type _ZSt19__iterator_categoryIPKcENSt15iterator_traitsIT_E17iterator_categoryERKS3_, @function
_ZSt19__iterator_categoryIPKcENSt15iterator_traitsIT_E17iterator_categoryERKS3_:
.LFB2132:
.cfi_startproc
endbr64
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
movq %rdi, -8(%rbp)
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE2132:
.size _ZSt19__iterator_categoryIPKcENSt15iterator_traitsIT_E17iterator_categoryERKS3_, .-_ZSt19__iterator_categoryIPKcENSt15iterator_traitsIT_E17iterator_categoryERKS3_
.section .text._ZSt10__distanceIPKcENSt15iterator_traitsIT_E15difference_typeES3_S3_St26random_access_iterator_tag,"axG",@progbits,_ZSt10__distanceIPKcENSt15iterator_traitsIT_E15difference_typeES3_S3_St26random_access_iterator_tag,comdat
.weak _ZSt10__distanceIPKcENSt15iterator_traitsIT_E15difference_typeES3_S3_St26random_access_iterator_tag
.type _ZSt10__distanceIPKcENSt15iterator_traitsIT_E15difference_typeES3_S3_St26random_access_iterator_tag, @function
_ZSt10__distanceIPKcENSt15iterator_traitsIT_E15difference_typeES3_S3_St26random_access_iterator_tag:
.LFB2133:
.cfi_startproc
endbr64
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
movq %rdi, -8(%rbp)
movq %rsi, -16(%rbp)
movq -16(%rbp), %rax
subq -8(%rbp), %rax
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE2133:
.size _ZSt10__distanceIPKcENSt15iterator_traitsIT_E15difference_typeES3_S3_St26random_access_iterator_tag, .-_ZSt10__distanceIPKcENSt15iterator_traitsIT_E15difference_typeES3_S3_St26random_access_iterator_tag
.text
.type _Z41__static_initialization_and_destruction_0ii, @function
_Z41__static_initialization_and_destruction_0ii:
.LFB2257:
.cfi_startproc
endbr64
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
subq $16, %rsp
movl %edi, -4(%rbp)
movl %esi, -8(%rbp)
cmpl $1, -4(%rbp)
jne .L68
cmpl $65535, -8(%rbp)
jne .L68
leaq _ZStL8__ioinit(%rip), %rax
movq %rax, %rdi
call _ZNSt8ios_base4InitC1Ev@PLT
leaq __dso_handle(%rip), %rax
movq %rax, %rdx
leaq _ZStL8__ioinit(%rip), %rax
movq %rax, %rsi
movq _ZNSt8ios_base4InitD1Ev@GOTPCREL(%rip), %rax
movq %rax, %rdi
call __cxa_atexit@PLT
.L68:
nop
leave
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE2257:
.size _Z41__static_initialization_and_destruction_0ii, .-_Z41__static_initialization_and_destruction_0ii
.type _GLOBAL__sub_I_main, @function
_GLOBAL__sub_I_main:
.LFB2258:
.cfi_startproc
endbr64
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
movl $65535, %esi
movl $1, %edi
call _Z41__static_initialization_and_destruction_0ii
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE2258:
.size _GLOBAL__sub_I_main, .-_GLOBAL__sub_I_main
.section .init_array,"aw"
.align 8
.quad _GLOBAL__sub_I_main
.hidden DW.ref.__gxx_personality_v0
.weak DW.ref.__gxx_personality_v0
.section .data.rel.local.DW.ref.__gxx_personality_v0,"awG",@progbits,DW.ref.__gxx_personality_v0,comdat
.align 8
.type DW.ref.__gxx_personality_v0, @object
.size DW.ref.__gxx_personality_v0, 8
DW.ref.__gxx_personality_v0:
.quad __gxx_personality_v0
.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:
关键点解析?
1. std::char_traits
的 length
方法
.section .text._ZNSt11char_traitsIcE6lengthEPKc,"axG",@progbits,_ZNSt11char_traitsIcE6lengthEPKc,comdat
.weak _ZNSt11char_traitsIcE6lengthEPKc
.type _ZNSt11char_traitsIcE6lengthEPKc, @function
_ZNSt11char_traitsIcE6lengthEPKc:
...
call strlen@PLT # 调用 strlen 函数计算字符串长度
...
????????此部分是 std::char_traits<char>::length
函数的实现,它使用 strlen
来获取 C 风格字符串的长度
2. Container<std::string>
的析构函数
.section .text._ZN9ContainerINSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEED2Ev,"axG",@progbits,_ZN9ContainerINSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEED5Ev,comdat
.weak _ZN9ContainerINSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEED2Ev
.type _ZN9ContainerINSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEED2Ev, @function
_ZN9ContainerINSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEED2Ev:
...
call _ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEED1Ev@PLT # 调用 std::string 的析构函数
...
????????这部分代码是模板类 Container<std::string>
的析构函数,其中调用了 std::string
的析构函数。
3. 主函数 main
.globl main
.type main, @function
main:
...
call _ZN9ContainerIiEC1Ei # 调用 Container<int> 的构造函数
...
call _ZNK9ContainerIiE4showEv # 调用 Container<int>::show
...
call _Z10printTwiceIiEvRK9ContainerIT_E # 调用 printTwice<int>
...
????????主函数中调用了 Container<int>
的构造函数和 show
方法,以及模板函数 printTwice<int>
4. Container<int>
类的 show
方法?
.section .text._ZNK9ContainerIiE4showEv,"axG",@progbits,_ZNK9ContainerIiE4showEv,comdat
.weak _ZNK9ContainerIiE4showEv
.type _ZNK9ContainerIiE4showEv, @function
_ZNK9ContainerIiE4showEv:
...
call _ZNSolsEi@PLT # 调用操作符 << 来输出 int
...
5. 模板函数 printTwice
.section .text._Z10printTwiceIiEvRK9ContainerIT_E,"axG",@progbits,_Z10printTwiceIiEvRK9ContainerIT_E,comdat
.weak _Z10printTwiceIiEvRK9ContainerIT_E
.type _Z10printTwiceIiEvRK9ContainerIT_E, @function
_Z10printTwiceIiEvRK9ContainerIT_E:
...
call _ZNK9ContainerIiE8getValueEv # 调用 Container<int>::getValue
...
????????这部分展示了如何调用 Container<int>::getValue
方法,作为 printTwice
函数的一部分,用于获取并打印容器中的值。
6.?Container<std::string>
的构造
????????让我们逐行细致地解析 Container<std::string>
的构造函数的汇编代码。这个构造函数是为 Container
模板类实例化的,其中模板参数为 std::string
。
.section .text._ZN9ContainerINSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEEC2ES5_,"axG",@progbits,_ZN9ContainerINSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEEC5ES5_,comdat
.align 2
.weak _ZN9ContainerINSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEEC2ES5_
.type _ZN9ContainerINSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEEC2ES5_, @function
_ZN9ContainerINSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEEC2ES5_:
.LFB2006:
.cfi_startproc
endbr64
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
subq $16, %rsp
movq %rdi, -8(%rbp)
movq %rsi, -16(%rbp)
movq -8(%rbp), %rax
movq -16(%rbp), %rdx
movq %rdx, %rsi
movq %rax, %rdi
call _ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEC1ERKS4_@PLT
nop
leave
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE2006:
解析
函数标签和序言:
.section?? ?...,"axG",@progbits,...,comdat
.align 2
.weak?? ?...
.type?? ?..., @function
_ZN9ContainerINSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEEC2ES5_:
.LFB2006:
?? ?.cfi_startproc
?? ?endbr64
?? ?pushq?? ?%rbp
这部分设置了函数的节(section)和对齐(align)。函数名为 _ZN9ContainerINSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEEC2ES5_
,这是由 C++ 名称改写(name mangling)生成的,表示 Container<std::string>
的构造函数。pushq %rbp
和后续指令设置了函数的栈帧。
设置栈帧:
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
subq $16, %rsp
处理参数:
movq %rdi, -8(%rbp)
movq %rsi, -16(%rbp)
????????将传递给构造函数的参数(通过 rdi
和 rsi
寄存器)存储在栈帧中。在 x86-64 调用约定中,rdi
和 rsi
通常用于传递前两个参数。
调用 std::string
的拷贝构造函数:
movq -8(%rbp), %rax
movq -16(%rbp), %rdx
movq %rdx, %rsi
movq %rax, %rdi
call _ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEC1ERKS4_@PLT
????????这里从栈中取出 Container
对象的地址和传递的 std::string
对象的地址,将它们分别放入 rdi
和 rsi
寄存器(按照 x86-64 的参数传递规则),然后调用 std::string
的拷贝构造函数。
函数的尾声和返回:
nop
leave
.cfi_def_cfa 7, 8
ret
?????????nop
是一个空指令,可能用于对齐。leave
指令清理栈帧,ret
执行函数返回。
此汇编代码段实现了 Container<std::string>
的构造函数,它主要调用 std::string
的拷贝构造函数来初始化其内部的 std::string
成员。这展示了 C++ 类模板如何在底层与复杂类型(如 std::string
)互动。
总结
-
C++ 程序与汇编代码的关系:开始时,我们探讨了如何将 C++ 代码转换成汇编代码,并通过实例分析了这一过程。您提供了几个 C++ 示例,包括基本函数和类定义,我为它们提供了汇编代码的详细解析。
-
汇编代码解析:对您提供的 C++ 代码(如
Container<int>
和Container<std::string>
的实现)的汇编版本进行了逐行分析。这包括了对函数调用、参数处理、构造函数和析构函数的汇编级解释。 -
模板和模板元编程:我们讨论了 C++ 模板的概念,特别是模板类和函数如何在编译时为不同的数据类型实例化。此外,还探讨了模板元编程(TMP)的概念及其在 C++ 中的应用,强调了编译时计算和类型操作的能力。
-
C++ 构造函数和析构函数的汇编表示:详细分析了
Container<std::string>
的构造函数在汇编级别的表示,包括函数的设置、参数处理、std::string
的拷贝构造函数调用以及函数结尾的处理。 -
实际汇编代码的细致解析:对实际汇编代码进行了详细的逐行解析,特别是针对
Container<std::string>
的构造函数。
整体而言,这次对话重点关注了 C++ 代码与其对应汇编代码之间的关系,尤其是涉及模板类和函数的实例化过程。这有助于更好地理解 C++ 代码在底层是如何实现的,以及编译器如何处理模板和其他复杂的 C++ 结构。
附录:段说明
??.weak
和 .type
是汇编器指令,用于提供有关符号(如函数或变量)的额外信息。这些指令在汇编代码中有特定的作用,尤其是在与链接器交互时。
-
.weak 指令:
.weak
指令用于声明一个弱符号。弱符号是一种特殊类型的符号,其特点是在链接时可以被同名的强符号(默认类型的符号)覆盖。- 如果多个模块(编译单元)中定义了同名的弱符号,链接器将选择其中的一个。
- 弱符号常用于库中,允许用户在自己的应用程序中重定义这些符号,而不会引起链接冲突。
- 示例:
.weak _ZN9ContainerINSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEEC2ES5_
表示Container<std::string>
的构造函数是一个弱符号。
-
.type 指令:
.type
指令用于为符号指定类型,通常是在定义函数或变量时使用。- 它告诉汇编器和链接器该符号是函数还是数据对象,以及相关的类型信息。
- 在 ELF(可执行和可链接格式)中,
.type
指令被用来协助动态链接器正确处理符号。 - 示例:
.type _ZN9ContainerINSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEEC2ES5_, @function
表示该符号是一个函数类型的符号。
指令和伪指令
????????汇编语言中存在许多指令和伪指令,用于指示汇编器如何处理代码和数据。以下是一些常见的汇编伪指令及其用途:?
-
.data:
- 用于声明初始化的数据段,通常用于存放程序中的全局变量。
-
.text:
- 指定随后的部分是代码段,主要用于存放程序指令。
-
.bss:
- 用于声明未初始化的数据段,通常用于定义不需要初始值的全局变量。
-
.global 或 .globl:
- 用于声明全局符号,使得符号(如函数或变量)对其他编译单元可见。
-
.align:
- 用于指定内存对齐,确保数据位于适当的内存地址。
-
.ascii:
- 用于定义一个包含 ASCII 字符串的数据项,但不自动在末尾添加空字符。
-
.asciz 或 .string:
- 类似于
.ascii
,但会在字符串末尾自动添加一个空字符。
- 类似于
-
.byte:
- 用于定义一个或多个字节的数据。
-
.word:
- 定义一个或多个字(通常为 2 字节)的数据。
-
.long 或 .int:
- 定义一个或多个长字(通常为 4 字节)的数据。
-
.quad:
- 定义一个或多个四字(通常为 8 字节)的数据。
-
.comm:
- 声明一个公共变量,通常用于定义未初始化的全局变量。
-
.extern:
- 表明接下来的符号在其他文件中定义。
-
.section:
- 指定一个新的段(section)或者切换到已有的段。
-
.type:
- 指定一个符号的类型,通常与函数或数据相关。
-
.size:
- 为符号指定大小。
-
.file:
- 用于指定源文件的名字。
-
.line:
- 用于调试,标注接下来的代码对应源文件中的行号。
-
.weak:
- 定义一个弱符号,这种符号在链接时可以被相同名字的强符号覆盖。
-
.equ 或 .set:
- 用于给一个数值或表达式设定一个符号名,类似于定义常量。
-
.macro 和 .endm:
- 用于定义宏,
.macro
开始宏定义,.endm
结束宏定义。
- 用于定义宏,
-
.include:
- 包含另一个文件的内容。
-
.if, .else, .endif:
- 用于条件汇编,根据条件包含或排除代码部分。
-
.rept 和 .endr:
- 重复一段代码多次,
.rept
开始重复,.endr
结束重复。
- 重复一段代码多次,
-
.pushsection 和 .popsection:
- 临时切换到一个新的段,并在完成后恢复原来的段。
-
.fill:
- 用指定的字节数和值填充区域。
-
.org:
- 指定接下来的代码或数据的起始地址。
-
.space 或 .skip:
- 分配一定数量的未初始化空间。
-
.def 和 .endef:
- 在某些汇编器中用于定义一个符号,开始和结束符号定义。
-
.lcomm:
- 定义局部公共变量,该变量在当前文件内部可见。
-
.save 和 .restore:
- 用于保存和恢复寄存器状态,常见于宏定义中。
-
.ref:
- 声明一个外部符号为引用,用于链接时的符号解析。
-
.ident:
- 用于在对象文件中放置识别信息,通常包含编译器版本或其他注释信息。
-
.version:
- 指定文件的版本。
-
.vtable:
- 在某些架构中用于构建虚函数表。
-
.sbss:
- 用于声明小型未初始化数据段,特别是对于全局变量而言。
-
.sdata:
- 用于声明小型已初始化数据段。
-
.p2align:
- 用于指定下一个数据或指令的对齐方式,通常用于性能优化。
-
.option:
- 设置汇编器的特定选项。
-
.thumb 和 .thumb_func:
- 在 ARM 架构中,指定接下来的代码使用 THUMB 指令集。
-
.arm:
- 在 ARM 架构中,指定接下来的代码使用标准 ARM 指令集。
-
.file:
- 用于调试信息中,指定源代码的文件名。
-
.line:
- 用于调试信息中,指定源代码的行号。
-
.hidden:
- 指定符号应该在 ELF 符号表中有 HIDDEN 属性,使其在动态链接中不可见。
-
.protected 和 .internal:
- 指定符号的链接可见性为 PROTECTED 或 INTERNAL。
-
.symver:
- 用于版本化符号,特别是在共享库中定义不同版本的 API。
-
.tbss 和 .tdata:
- 用于声明线程局部存储(TLS)中的未初始化(.tbss)和初始化(.tdata)数据。
-
.cfi_ 指令*:
- 一系列指令,用于提供调用帧信息,帮助异常处理和堆栈展开。
????????这些伪指令和指令提供了对汇编过程的细粒度控制,允许程序员指定与调试信息、性能优化、特定架构特性以及链接行为相关的详细信息。这些指令在高级汇编编程和系统级编程中非常重要,特别是在操作系统、驱动程序以及需要直接硬件控制的应用中。
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!