源码1 函数之间值传递
#include <iostream>
// 定义一个函数,计算两个整数的和
int add(int a, int b) {
return a + b;
}
int main() {
int num1 = 5;
int num2 = 10;
// 调用 add 函数,传入 num1 和 num2 的值
int sum = add(num1, num2);
// 输出结果
std::cout << "The sum of " << num1 << " and " << num2 << " is: " << sum << std::endl;
return 0;
}
源码1对应的汇编
.file "simple_function.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 of "
.LC1:
.string " and "
.LC2:
.string " 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 $5, -12(%rbp)
movl $10, -8(%rbp)
movl -8(%rbp), %edx
movl -12(%rbp), %eax
movl %edx, %esi
movl %eax, %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 -12(%rbp), %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
movl -8(%rbp), %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 %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:
总结
main函数核心调用add部分:
movl $5, -12(%rbp) ;将立即数5移动到栈帧中相对于 %rbp 偏移 -12 字节的位置,即初始化局部变量 num1 为 5。
movl $10, -8(%rbp) ;将立即数10移动到栈帧中相对于 %rbp 偏移 -8 字节的位置,即初始化局部变量 num2 为 10。
movl -8(%rbp), %edx ;将 num2 的值从栈帧中加载到寄存器 %edx。
movl -12(%rbp), %eax ;将 num1 的值从栈帧中加载到寄存器 %eax。
movl %edx, %esi ;将 %edx (即 num2) 的值移动到寄存器 %esi,作为 add 函数的第二个参数。
movl %eax, %edi ;将 %eax (即 num1) 的值移动到寄存器 %edi,作为 add 函数的第一个参数。
call _Z3addii ;调用add函数
add被调用的核心部分:
movl %edi, -4(%rbp) ;将第一个参数(存储在寄存器%edi中)移动到栈帧中相对于%rbp偏移-4字节的位置。
movl %esi, -8(%rbp) ;将第二个参数(存储在寄存器%esi中)移动到栈帧中相对于%rbp偏移-8字节的位置。
movl -4(%rbp), %edx ;将第一个参数从栈帧中加载到寄存器%edx
movl -8(%rbp), %eax ;将第二个参数从栈帧中加载到寄存器%eax
addl %edx, %eax ;将%edx和%eax中的值相加,结果存储在%eax中
最终是将edi和esi中的值拷贝到eax和edx中进行操作的。
源码2 函数之间引用传递
#include <iostream>
// 定义一个函数,计算两个整数的和,并通过引用修改传入的参数
void add(int& a, int& b, int& result) {
result = a + b;
}
int main() {
int num1 = 5;
int num2 = 10;
int sum; // 用于存储结果的变量
std::cout << "Before add: num1 = " << num1 << ", num2 = " << num2 << std::endl;
// 调用 add 函数,传入 num1、num2 和 sum 的引用
add(num1, num2, sum);
std::cout << "After add: num1 = " << num1 << ", num2 = " << num2 << ", sum = " << sum << std::endl;
return 0;
}
源码2对应的汇编
.file "add_by_reference.cpp"
.text
.local _ZStL8__ioinit
.comm _ZStL8__ioinit,1,1
.globl _Z3addRiS_S_
.type _Z3addRiS_S_, @function
_Z3addRiS_S_:
.LFB1731:
.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 %rdx, -24(%rbp)
movq -8(%rbp), %rax
movl (%rax), %edx
movq -16(%rbp), %rax
movl (%rax), %eax
addl %eax, %edx
movq -24(%rbp), %rax
movl %edx, (%rax)
nop
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE1731:
.size _Z3addRiS_S_, .-_Z3addRiS_S_
.section .rodata
.LC0:
.string "After add: num1 = "
.LC1:
.string ", num2 = "
.LC2:
.string ", sum = "
.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 $32, %rsp
movq %fs:40, %rax
movq %rax, -8(%rbp)
xorl %eax, %eax
movl $5, -20(%rbp)
movl $10, -16(%rbp)
leaq -12(%rbp), %rdx
leaq -16(%rbp), %rcx
leaq -20(%rbp), %rax
movq %rcx, %rsi
movq %rax, %rdi
call _Z3addRiS_S_
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 -20(%rbp), %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
movl -16(%rbp), %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 %rax, %rdx
movl -12(%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
movq -8(%rbp), %rdx
subq %fs:40, %rdx
je .L4
call __stack_chk_fail@PLT
.L4:
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__Z3addRiS_S_, @function
_GLOBAL__sub_I__Z3addRiS_S_:
.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__Z3addRiS_S_, .-_GLOBAL__sub_I__Z3addRiS_S_
.section .init_array,"aw"
.align 8
.quad _GLOBAL__sub_I__Z3addRiS_S_
.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:
总结
main函数调用部分的核心代码:
leaq是 "Load Effective Address quadword" 的缩写。leaq指令的主要作用是将一个内存地址计算出来,并将其加载到一个 64 位寄存器中。
movl $5, -20(%rbp) ;将 num1 初始化为 5,存储在 rbp - 20 的位置。
movl $10, -16(%rbp) ;将 num2 初始化为 10,存储在 rbp - 16 的位置。
leaq -12(%rbp), %rdx ;将 sum 的地址放入 rdx 寄存器。
leaq -16(%rbp), %rcx ;将 num2 的地址放入 rcx 寄存器。
leaq -20(%rbp), %rax ;将 num1 的地址放入 rax 寄存器。
movq %rcx, %rsi ;将 num2 的地址从 rcx 复制到 rsi 寄存器,作为第二个参数传递给 add
movq %rax, %rdi ;将 num1 的地址从 rax 复制到 rdi 寄存器,作为第一个参数传递给 add
call _Z3addRiS_S_ ;调用add函数
add函数的执行部分:
movq %rdi, -8(%rbp) ;将第一个参数(num1 的地址)存储到 rbp - 8 的位置。
movq %rsi, -16(%rbp) ;将第二个参数(num2 的地址)存储到 rbp - 16 的位置。
movq %rdx, -24(%rbp) ;将第三个参数(sum 的地址)存储到 rbp - 24 的位置。
movq -8(%rbp), %rax ;将 num1 的地址加载到 rax 寄存器。
movl (%rax), %edx ;将 num1 的值(通过解引用地址)加载到 edx 寄存器。
movq -16(%rbp), %rax ;将 num2 的地址加载到 rax 寄存器。
movl (%rax), %eax ;将 num2 的值加载到 eax 寄存器。
addl %eax, %edx ;将 eax 和 edx 的值相加,结果存入 edx。
movq -24(%rbp) ;%rax ;将 sum 的地址加载到 rax 寄存器。
movl %edx, (%rax) ;将计算结果(sum 的值)存储到 sum 的地址所指向的内存位置。
兜兜转转了一圈,引用传递实际上传输的是地址,操作的是该地址对应的值。所以被调用的函数会改变调用方的值。
源码3 模板函数
#include <iostream>
// 定义一个模板函数,计算两个任意类型数据的和
template <typename T>
T add(T a, T b) {
return a + b;
}
int main() {
int num1 = 5;
int num2 = 10;
// 调用 add 函数,传入 int 类型数据
int sum_int = add(num1, num2);
double num3 = 3.14;
double num4 = 2.71;
// 调用 add 函数,传入 double 类型数据
double sum_double = add(num3, num4);
std::cout << "The sum of " << num3 << " and " << num4 << " is: " << sum_double << std::endl;
return 0;
}
源码3对应汇编
.file "template_function.cpp"
.text
.local _ZStL8__ioinit
.comm _ZStL8__ioinit,1,1
.section .rodata
.LC2:
.string "The sum of "
.LC3:
.string " and "
.LC4:
.string " 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 $48, %rsp
movl $5, -36(%rbp)
movl $10, -32(%rbp)
movl -32(%rbp), %edx
movl -36(%rbp), %eax
movl %edx, %esi
movl %eax, %edi
call _Z3addIiET_S0_S0_
movl %eax, -28(%rbp)
movsd .LC0(%rip), %xmm0
movsd %xmm0, -24(%rbp)
movsd .LC1(%rip), %xmm0
movsd %xmm0, -16(%rbp)
movsd -16(%rbp), %xmm0
movq -24(%rbp), %rax
movapd %xmm0, %xmm1
movq %rax, %xmm0
call _Z3addIdET_S0_S0_
movq %xmm0, %rax
movq %rax, -8(%rbp)
leaq .LC2(%rip), %rax
movq %rax, %rsi
leaq _ZSt4cout(%rip), %rax
movq %rax, %rdi
call _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc@PLT
movq %rax, %rdx
movq -24(%rbp), %rax
movq %rax, %xmm0
movq %rdx, %rdi
call _ZNSolsEd@PLT
movq %rax, %rdx
leaq .LC3(%rip), %rax
movq %rax, %rsi
movq %rdx, %rdi
call _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc@PLT
movq %rax, %rdx
movq -16(%rbp), %rax
movq %rax, %xmm0
movq %rdx, %rdi
call _ZNSolsEd@PLT
movq %rax, %rdx
leaq .LC4(%rip), %rax
movq %rax, %rsi
movq %rdx, %rdi
call _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc@PLT
movq %rax, %rdx
movq -8(%rbp), %rax
movq %rax, %xmm0
movq %rdx, %rdi
call _ZNSolsEd@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
.section .text._Z3addIiET_S0_S0_,"axG",@progbits,_Z3addIiET_S0_S0_,comdat
.weak _Z3addIiET_S0_S0_
.type _Z3addIiET_S0_S0_, @function
_Z3addIiET_S0_S0_:
.LFB1993:
.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
.LFE1993:
.size _Z3addIiET_S0_S0_, .-_Z3addIiET_S0_S0_
.section .text._Z3addIdET_S0_S0_,"axG",@progbits,_Z3addIdET_S0_S0_,comdat
.weak _Z3addIdET_S0_S0_
.type _Z3addIdET_S0_S0_, @function
_Z3addIdET_S0_S0_:
.LFB1994:
.cfi_startproc
endbr64
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
movsd %xmm0, -8(%rbp)
movsd %xmm1, -16(%rbp)
movsd -8(%rbp), %xmm0
addsd -16(%rbp), %xmm0
movq %xmm0, %rax
movq %rax, %xmm0
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE1994:
.size _Z3addIdET_S0_S0_, .-_Z3addIdET_S0_S0_
.text
.type _Z41__static_initialization_and_destruction_0ii, @function
_Z41__static_initialization_and_destruction_0ii:
.LFB2238:
.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
.LFE2238:
.size _Z41__static_initialization_and_destruction_0ii, .-_Z41__static_initialization_and_destruction_0ii
.type _GLOBAL__sub_I_main, @function
_GLOBAL__sub_I_main:
.LFB2239:
.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
.LFE2239:
.size _GLOBAL__sub_I_main, .-_GLOBAL__sub_I_main
.section .init_array,"aw"
.align 8
.quad _GLOBAL__sub_I_main
.section .rodata
.align 8
.LC0:
.long 1374389535
.long 1074339512
.align 8
.LC1:
.long 2061584302
.long 1074114068
.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:
总结
模板函数不为特定数据类型编写,而是定义了一个通用算法框架。在编译时,根据传入的实际数据类型,编译器会生成对应的函数实例。
模板函数的多态性在编译时确定,不同于虚函数的运行时多态。
模板函数实例化 (_Z3addIiET_S0_S0_, _Z3addIdET_S0_S0_):
- 汇编代码中出现了两个函数:
_Z3addIiET_S0_S0_(对应add<int>) 和_Z3addIdET_S0_S0_(对应add<double>)。它们是编译器根据传入的int和double类型分别实例化的add模板函数。 - 从实例化函数的汇编可以看出,它们的核心逻辑非常相似:获取参数、执行加法操作、返回结果。只不过一个是整数加法 (
addl),一个是浮点数加法 (addsd)。这体现了模板函数的通用算法框架。
main 函数调用 (call _Z3addIiET_S0_S0_, call _Z3addIdET_S0_S0_):
main函数中,对add函数的两次调用实际上是调用了对应的模板函数实例。编译器在编译时已经根据参数类型确定了要调用的具体函数实例。
源码4 虚函数 运行时确定调用对象
// can show dynamic calling process
#include <iostream>
class Base {
public:
virtual void func() {
std::cout << "Base::func()\n";
}
};
class Derived : public Base {
public:
void func() override {
std::cout << "Derived::func()\n";
}
};
int main() {
Base *b = new Derived();
b->func(); // 虚函数调用
delete b;
return 0;
}
源码4对应的汇编
.file "virtual.cpp"
.text
.local _ZStL8__ioinit
.comm _ZStL8__ioinit,1,1
.section .rodata
.LC0:
.string "Base::func()\n"
.section .text._ZN4Base4funcEv,"axG",@progbits,_ZN4Base4funcEv,comdat
.align 2
.weak _ZN4Base4funcEv
.type _ZN4Base4funcEv, @function
_ZN4Base4funcEv:
.LFB1731:
.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
nop
leave
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE1731:
.size _ZN4Base4funcEv, .-_ZN4Base4funcEv
.section .rodata
.LC1:
.string "Derived::func()\n"
.section .text._ZN7Derived4funcEv,"axG",@progbits,_ZN7Derived4funcEv,comdat
.align 2
.weak _ZN7Derived4funcEv
.type _ZN7Derived4funcEv, @function
_ZN7Derived4funcEv:
.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 .LC1(%rip), %rax
movq %rax, %rsi
leaq _ZSt4cout(%rip), %rax
movq %rax, %rdi
call _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc@PLT
nop
leave
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE1732:
.size _ZN7Derived4funcEv, .-_ZN7Derived4funcEv
.section .text._ZN4BaseC2Ev,"axG",@progbits,_ZN4BaseC5Ev,comdat
.align 2
.weak _ZN4BaseC2Ev
.type _ZN4BaseC2Ev, @function
_ZN4BaseC2Ev:
.LFB1736:
.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)
leaq 16+_ZTV4Base(%rip), %rdx
movq -8(%rbp), %rax
movq %rdx, (%rax)
nop
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE1736:
.size _ZN4BaseC2Ev, .-_ZN4BaseC2Ev
.weak _ZN4BaseC1Ev
.set _ZN4BaseC1Ev,_ZN4BaseC2Ev
.section .text._ZN7DerivedC2Ev,"axG",@progbits,_ZN7DerivedC5Ev,comdat
.align 2
.weak _ZN7DerivedC2Ev
.type _ZN7DerivedC2Ev, @function
_ZN7DerivedC2Ev:
.LFB1738:
.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 _ZN4BaseC2Ev
leaq 16+_ZTV7Derived(%rip), %rdx
movq -8(%rbp), %rax
movq %rdx, (%rax)
nop
leave
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE1738:
.size _ZN7DerivedC2Ev, .-_ZN7DerivedC2Ev
.weak _ZN7DerivedC1Ev
.set _ZN7DerivedC1Ev,_ZN7DerivedC2Ev
.text
.globl main
.type main, @function
main:
.LFB1733:
.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
movl $8, %edi
call _Znwm@PLT
movq %rax, %rbx
movq $0, (%rbx)
movq %rbx, %rdi
call _ZN7DerivedC1Ev
movq %rbx, -24(%rbp)
movq -24(%rbp), %rax
movq (%rax), %rax
movq (%rax), %rdx
movq -24(%rbp), %rax
movq %rax, %rdi
call *%rdx
movq -24(%rbp), %rax
testq %rax, %rax
je .L6
movl $8, %esi
movq %rax, %rdi
call _ZdlPvm@PLT
.L6:
movl $0, %eax
movq -8(%rbp), %rbx
leave
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE1733:
.size main, .-main
.weak _ZTV7Derived
.section .data.rel.ro.local._ZTV7Derived,"awG",@progbits,_ZTV7Derived,comdat
.align 8
.type _ZTV7Derived, @object
.size _ZTV7Derived, 24
_ZTV7Derived:
.quad 0
.quad _ZTI7Derived
.quad _ZN7Derived4funcEv
.weak _ZTV4Base
.section .data.rel.ro.local._ZTV4Base,"awG",@progbits,_ZTV4Base,comdat
.align 8
.type _ZTV4Base, @object
.size _ZTV4Base, 24
_ZTV4Base:
.quad 0
.quad _ZTI4Base
.quad _ZN4Base4funcEv
.weak _ZTI7Derived
.section .data.rel.ro._ZTI7Derived,"awG",@progbits,_ZTI7Derived,comdat
.align 8
.type _ZTI7Derived, @object
.size _ZTI7Derived, 24
_ZTI7Derived:
.quad _ZTVN10__cxxabiv120__si_class_type_infoE+16
.quad _ZTS7Derived
.quad _ZTI4Base
.weak _ZTS7Derived
.section .rodata._ZTS7Derived,"aG",@progbits,_ZTS7Derived,comdat
.align 8
.type _ZTS7Derived, @object
.size _ZTS7Derived, 9
_ZTS7Derived:
.string "7Derived"
.weak _ZTI4Base
.section .data.rel.ro._ZTI4Base,"awG",@progbits,_ZTI4Base,comdat
.align 8
.type _ZTI4Base, @object
.size _ZTI4Base, 16
_ZTI4Base:
.quad _ZTVN10__cxxabiv117__class_type_infoE+16
.quad _ZTS4Base
.weak _ZTS4Base
.section .rodata._ZTS4Base,"aG",@progbits,_ZTS4Base,comdat
.type _ZTS4Base, @object
.size _ZTS4Base, 6
_ZTS4Base:
.string "4Base"
.text
.type _Z41__static_initialization_and_destruction_0ii, @function
_Z41__static_initialization_and_destruction_0ii:
.LFB2237:
.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 .L10
cmpl $65535, -8(%rbp)
jne .L10
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
.L10:
nop
leave
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE2237:
.size _Z41__static_initialization_and_destruction_0ii, .-_Z41__static_initialization_and_destruction_0ii
.type _GLOBAL__sub_I_main, @function
_GLOBAL__sub_I_main:
.LFB2238:
.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
.LFE2238:
.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:
总结
main:
.LFB1733: # 函数开始标签
.cfi_startproc
endbr64 # 用于增强代码安全性,防止控制流劫持
pushq %rbp # 保存基址寄存器 %rbp 的值到栈上
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp # 将栈顶指针 %rsp 赋值给 %rbp,作为新的基址
.cfi_def_cfa_register 6
pushq %rbx # 保存通用寄存器 %rbx 的值到栈上
subq $24, %rsp # 调整栈指针,为局部变量分配 24 字节空间
.cfi_offset 3, -24
# --- 1. 分配内存 ---
movl $8, %edi # 将要分配的内存大小 (8 字节) 放入寄存器 %edi
call _Znwm@PLT # %edi会被当做该函数的参数。调用 _Znwm 函数 (operator new) 分配内存,返回地址存入 %rax
# --- 2. 存储 vtable 指针 ---
movq %rax, %rbx # 将分配的内存地址保存到寄存器 %rbx 中
movq $0, (%rbx) # 将虚表指针置为 0 (这将在构造函数中被正确设置)
# --- 3. 构造对象 ---
movq %rbx, %rdi # 将对象地址 (现在是 %rbx) 放入寄存器 %rdi,作为 this 指针传给构造函数,new 出来的这块内存给赋给 this。js也是这样。
call _ZN7DerivedC1Ev # 调用 Derived 类的构造函数,new出的那块内存的内存布局,其中包括虚表指针。
# --- 4. 存储对象指针到栈 ---
movq %rbx, -24(%rbp) # 将对象地址存储到栈上,便于后续使用
# --- 5. 虚函数调用 ---
movq -24(%rbp), %rax # 从栈中加载对象地址到 %rax
movq (%rax), %rax # 获取对象的虚表指针
movq (%rax), %rdx # 获取虚表中第一个虚函数 (func) 的地址
movq -24(%rbp), %rax # 再次加载对象地址,作为 this 指针
movq %rax, %rdi # 将 this 指针放入 %rdi,%rdi稍后会在被调函数中当做参数使用
call *%rdx # 调用虚函数,至此我们可以看到rdx是通过计算得到的。计算逻辑为:new 出一块内存,this指向的就是这块内存-->根据代码逻辑调用对应的构造函数初始化this指向的这块内存,其中初始化过程中会将虚表指针初始化为这块内存的前8个字节-->获取this对象的前8个字节,这8个字节记录着虚表指针-->根据虚表指针解析到虚表指针对应的位置。
# --- 6. 释放内存 ---
movq -24(%rbp), %rax # 加载对象地址
testq %rax, %rax # 检查对象地址是否为 NULL
je .L6 # 如果是 NULL,跳转到 .L6
movl $8, %esi # 将要释放的内存大小 (8 字节) 放入寄存器 %esi
movq %rax, %rdi # 将对象地址放入 %rdi
call _ZdlPvm@PLT # 调用 _ZdlPvm 函数 (operator delete) 释放内存
.L6: # .L6 标签
movl $0, %eax # 将返回值设置为 0
movq -8(%rbp), %rbx # 恢复 %rbx 的值
leave # 恢复栈帧
.cfi_def_cfa 7, 8
ret # 函数返回
所谓虚函数的调用是在运行时确定的,站在汇编的角度,意思是具体调用哪个函数是根据this指向中的vptr确定的,但是this又是根据构造函数确定的,所以只有在执行了构造函数以后,this对应的那块内存才能被初始化完成。