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

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

目录

案例一 基础

C++ 代码示例

生成汇编代码

汇编代码解析

        注释解析 

        C语言版的汇编

        G++与GCC区别

        总结

案例二 类

总结 

案例三 虚函数和继承

伪代码解析

1. 虚函数表(VTable)

2. 对象构造和虚函数表指针

3. 调用虚函数

4. 多态的实现

总结

实际汇编

案例四 模板类

发现的问题解析

1. 编译器的自动实例化

2. 显式实例化

3. 代码膨胀问题

4. 编译时间增加

5. 类型特征和条件编译

6. 模板元编程

实际汇编

关键点解析 

1. std::char_traits 的 length 方法

2. Container 的析构函数

3. 主函数 main

4. Container 类的 show 方法 

5. 模板函数 printTwice

6. Container 的构造

解析

总结

附录:段说明

指令和伪指令


案例一 基础

C++ 代码示例

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

#include 

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 

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区别

  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 

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++代码如何转换为底层机器码提供了重要的启示:

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

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

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

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

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

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

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

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

案例三 虚函数和继承

        代码:

#include 

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. 对象构造和虚函数表指针

        当创建一个对象时(例如 DogCat 对象),构造函数会设置对象的虚函数表指针(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 的实际对象类型(DogCat),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::makeSoundCat::makeSound 方法中的输出字符串定义了常量。
  • Dog::makeSoundCat::makeSound 方法:这两个方法的实现在汇编中类似,主要区别在于加载的字符串常量不同。这些方法使用标准输出流 std::cout 打印各自的字符串。
  • makeAnimalSound 函数:这个函数展示了多态的实现。它首先从传入的 Animal 类型对象中获取虚函数表指针,然后从虚函数表中获取 makeSound 方法的地址,并通过这个地址调用方法。
  • main 函数:在 main 函数中,创建 DogCat 类型的对象,并通过 makeAnimalSound 函数演示多态的行为。

案例四 模板类

#include 

template
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
void printTwice(const Container& container) {
    std::cout << container.getValue() << " " << container.getValue() << std::endl;
}

int main() {
    Container intContainer(42);
    Container stringContainer("Hello, world!");

    intContainer.show();
    stringContainer.show();

    printTwice(intContainer);
    printTwice(stringContainer);

    return 0;
}

伪代码汇编:

# Container 类的构造函数
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 的 show 方法
Container_int_show:
    push rbp
    mov rbp, rsp
    mov rax, [rbp-16]  # 加载 this 指针
    mov ecx, [rax]     # 加载 this->value
    ; 调用标准库输出打印 ecx
    pop rbp
    ret

# Container 类的构造函数
Container_string_Constructor:
    ; 类似于 Container,但处理字符串对象

# Container 的 show 方法
Container_string_show:
    ; 类似于 Container 的 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 对象
    sub rsp, 24       # 为 Container 分配空间
    mov esi, 42       # 整数值
    lea rdi, [rsp+16] # 对象地址
    call Container_int_Constructor

    ; 创建 Container 对象
    sub rsp, 48       # 为 Container 分配空间
    ; 初始化字符串
    lea rdi, [rsp]    # 对象地址
    call Container_string_Constructor

    ; 调用 show 方法
    lea rdi, [rsp+16] # Container 对象地址
    call Container_int_show
    lea rdi, [rsp]    # Container 对象地址
    call Container_string_show

    ; 调用 printTwice 函数
    lea rdi, [rsp+16] # Container 对象地址
    call template_printTwice
    lea rdi, [rsp]    # Container 对象地址
    call template_printTwice

    leave
    ret

关键点解释:

  • 模板实例化:针对不同类型(如 intstd::string),编译器生成了不同的函数实例。这些函数实例在汇编代码中具有相似的结构,但处理的数据类型不同。
  • 构造函数:每个模板实例的构造函数初始化其成员变量。
  • 成员函数show 方法根据不同的模板实例进行了实现,以适应不同类型的成员变量。
  • 模板函数调用printTwice 函数显示了泛型函数的调用方式,根据传入对象的类型处理输出。

发现的问题解析

        当C++模板类或函数需要为许多不同的类型实例化时,编译器会为每个使用到的具体类型生成一个单独的实例。这是模板实例化的基本机制。以下是处理多种类型实例化时的几个要点和策略:

1. 编译器的自动实例化

        对于模板类或函数,编译器会在编译时自动为每个使用到的类型生成一个实例。例如,如果你的代码中使用了 ContainerContainerContainer,编译器将为这三种类型生成三个不同的实例。

2. 显式实例化

        在某些情况下,你可能想要显式地指定模板的实例化,尤其是当模板的定义和使用分布在不同的源文件中时。你可以在一个源文件中定义模板,然后在另一个源文件中显式实例化它,如下所示:

template class Container;  // 显式实例化 Container
template class Container;  // 显式实例化 Container

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_traitslength 方法

.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::length 函数的实现,它使用 strlen 来获取 C 风格字符串的长度

2. Container 的析构函数

.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 的析构函数。

3. 主函数 main

.globl	main
.type	main, @function
main:
    ...
    call	_ZN9ContainerIiEC1Ei  # 调用 Container 的构造函数
    ...
    call	_ZNK9ContainerIiE4showEv  # 调用 Container::show
    ...
    call	_Z10printTwiceIiEvRK9ContainerIT_E  # 调用 printTwice
    ...

        主函数中调用了 Container 的构造函数和 show 方法,以及模板函数 printTwice

4. Container 类的 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::getValue
    ...

        这部分展示了如何调用 Container::getValue 方法,作为 printTwice 函数的一部分,用于获取并打印容器中的值。

6. Container 的构造

        让我们逐行细致地解析 Container 的构造函数的汇编代码。这个构造函数是为 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 的构造函数。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)

        将传递给构造函数的参数(通过 rdirsi 寄存器)存储在栈帧中。在 x86-64 调用约定中,rdirsi 通常用于传递前两个参数。

调用 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 对象的地址,将它们分别放入 rdirsi 寄存器(按照 x86-64 的参数传递规则),然后调用 std::string 的拷贝构造函数。

函数的尾声和返回:

	nop
	leave
	.cfi_def_cfa 7, 8
	ret

         nop 是一个空指令,可能用于对齐。leave 指令清理栈帧,ret 执行函数返回。

此汇编代码段实现了 Container 的构造函数,它主要调用 std::string 的拷贝构造函数来初始化其内部的 std::string 成员。这展示了 C++ 类模板如何在底层与复杂类型(如 std::string)互动。

总结

  1. C++ 程序与汇编代码的关系:开始时,我们探讨了如何将 C++ 代码转换成汇编代码,并通过实例分析了这一过程。您提供了几个 C++ 示例,包括基本函数和类定义,我为它们提供了汇编代码的详细解析。

  2. 汇编代码解析:对您提供的 C++ 代码(如 ContainerContainer 的实现)的汇编版本进行了逐行分析。这包括了对函数调用、参数处理、构造函数和析构函数的汇编级解释。

  3. 模板和模板元编程:我们讨论了 C++ 模板的概念,特别是模板类和函数如何在编译时为不同的数据类型实例化。此外,还探讨了模板元编程(TMP)的概念及其在 C++ 中的应用,强调了编译时计算和类型操作的能力。

  4. C++ 构造函数和析构函数的汇编表示:详细分析了 Container 的构造函数在汇编级别的表示,包括函数的设置、参数处理、std::string 的拷贝构造函数调用以及函数结尾的处理。

  5. 实际汇编代码的细致解析:对实际汇编代码进行了详细的逐行解析,特别是针对 Container 的构造函数。

整体而言,这次对话重点关注了 C++ 代码与其对应汇编代码之间的关系,尤其是涉及模板类和函数的实例化过程。这有助于更好地理解 C++ 代码在底层是如何实现的,以及编译器如何处理模板和其他复杂的 C++ 结构。

附录:段说明

  .weak.type 是汇编器指令,用于提供有关符号(如函数或变量)的额外信息。这些指令在汇编代码中有特定的作用,尤其是在与链接器交互时。

  1. .weak 指令:

    • .weak 指令用于声明一个弱符号。弱符号是一种特殊类型的符号,其特点是在链接时可以被同名的强符号(默认类型的符号)覆盖。
    • 如果多个模块(编译单元)中定义了同名的弱符号,链接器将选择其中的一个。
    • 弱符号常用于库中,允许用户在自己的应用程序中重定义这些符号,而不会引起链接冲突。
    • 示例:.weak _ZN9ContainerINSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEEC2ES5_ 表示 Container 的构造函数是一个弱符号。
  2. .type 指令:

    • .type 指令用于为符号指定类型,通常是在定义函数或变量时使用。
    • 它告诉汇编器和链接器该符号是函数还是数据对象,以及相关的类型信息。
    • 在 ELF(可执行和可链接格式)中,.type 指令被用来协助动态链接器正确处理符号。
    • 示例:.type _ZN9ContainerINSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEEC2ES5_, @function 表示该符号是一个函数类型的符号。

指令和伪指令

        汇编语言中存在许多指令和伪指令,用于指示汇编器如何处理代码和数据。以下是一些常见的汇编伪指令及其用途: 

  1. .data

    • 用于声明初始化的数据段,通常用于存放程序中的全局变量。
  2. .text

    • 指定随后的部分是代码段,主要用于存放程序指令。
  3. .bss

    • 用于声明未初始化的数据段,通常用于定义不需要初始值的全局变量。
  4. .global.globl

    • 用于声明全局符号,使得符号(如函数或变量)对其他编译单元可见。
  5. .align

    • 用于指定内存对齐,确保数据位于适当的内存地址。
  6. .ascii

    • 用于定义一个包含 ASCII 字符串的数据项,但不自动在末尾添加空字符。
  7. .asciz.string

    • 类似于 .ascii,但会在字符串末尾自动添加一个空字符。
  8. .byte

    • 用于定义一个或多个字节的数据。
  9. .word

    • 定义一个或多个字(通常为 2 字节)的数据。
  10. .long.int

    • 定义一个或多个长字(通常为 4 字节)的数据。
  11. .quad

    • 定义一个或多个四字(通常为 8 字节)的数据。
  12. .comm

    • 声明一个公共变量,通常用于定义未初始化的全局变量。
  13. .extern

    • 表明接下来的符号在其他文件中定义。
  14. .section

    • 指定一个新的段(section)或者切换到已有的段。
  15. .type

    • 指定一个符号的类型,通常与函数或数据相关。
  16. .size

    • 为符号指定大小。
  17. .file

    • 用于指定源文件的名字。
  18. .line

    • 用于调试,标注接下来的代码对应源文件中的行号。
  19. .weak

    • 定义一个弱符号,这种符号在链接时可以被相同名字的强符号覆盖。
  20. .equ.set

    • 用于给一个数值或表达式设定一个符号名,类似于定义常量。
  21. .macro.endm

    • 用于定义宏,.macro 开始宏定义,.endm 结束宏定义。
  22. .include

    • 包含另一个文件的内容。
  23. .if, .else, .endif

    • 用于条件汇编,根据条件包含或排除代码部分。
  24. .rept.endr

    • 重复一段代码多次,.rept 开始重复,.endr 结束重复。
  25. .pushsection.popsection

    • 临时切换到一个新的段,并在完成后恢复原来的段。
  26. .fill

    • 用指定的字节数和值填充区域。
  27. .org

    • 指定接下来的代码或数据的起始地址。
  28. .space.skip

    • 分配一定数量的未初始化空间。
  29. .def.endef

    • 在某些汇编器中用于定义一个符号,开始和结束符号定义。
  30. .lcomm

    • 定义局部公共变量,该变量在当前文件内部可见。
  31. .save.restore

    • 用于保存和恢复寄存器状态,常见于宏定义中。
  32. .ref

    • 声明一个外部符号为引用,用于链接时的符号解析。
  33. .ident

    • 用于在对象文件中放置识别信息,通常包含编译器版本或其他注释信息。
  34. .version

    • 指定文件的版本。
  35. .vtable

    • 在某些架构中用于构建虚函数表。
  36. .sbss

    • 用于声明小型未初始化数据段,特别是对于全局变量而言。
  37. .sdata

    • 用于声明小型已初始化数据段。
  38. .p2align

    • 用于指定下一个数据或指令的对齐方式,通常用于性能优化。
  39. .option

    • 设置汇编器的特定选项。
  40. .thumb.thumb_func

    • 在 ARM 架构中,指定接下来的代码使用 THUMB 指令集。
  41. .arm

    • 在 ARM 架构中,指定接下来的代码使用标准 ARM 指令集。
  42. .file

    • 用于调试信息中,指定源代码的文件名。
  43. .line

    • 用于调试信息中,指定源代码的行号。
  44. .hidden

    • 指定符号应该在 ELF 符号表中有 HIDDEN 属性,使其在动态链接中不可见。
  45. .protected.internal

    • 指定符号的链接可见性为 PROTECTED 或 INTERNAL。
  46. .symver

    • 用于版本化符号,特别是在共享库中定义不同版本的 API。
  47. .tbss.tdata

    • 用于声明线程局部存储(TLS)中的未初始化(.tbss)和初始化(.tdata)数据。
  48. .cfi_ 指令*:

    • 一系列指令,用于提供调用帧信息,帮助异常处理和堆栈展开。

        这些伪指令和指令提供了对汇编过程的细粒度控制,允许程序员指定与调试信息、性能优化、特定架构特性以及链接行为相关的详细信息。这些指令在高级汇编编程和系统级编程中非常重要,特别是在操作系统、驱动程序以及需要直接硬件控制的应用中。

你可能感兴趣的:(汇编语言,c语言,c++,汇编)