【汇编】C++ 函数调用分析之——无参调用。

由于C++函数调用多种多样,本文将分几个章节分别讲解。

首先来最简单的一个无参数调用。

有如下测试代码:


void func(){
	//do nothing here
}


int main(int argc,char *argv[])
{
	//call func
	func();
	return 0;
}
在主函数中调用自定义func函数。


再看看两个函数的汇编码:


void func(){
//显然和之前讲过的main的实现雷同,挖一个192字节栈空间,然后填int 3中断(清cc)
002F13A0  push        ebp  
002F13A1  mov         ebp,esp  
002F13A3  sub         esp,0C0h  
002F13A9  push        ebx  
002F13AA  push        esi  
002F13AB  push        edi  
002F13AC  lea         edi,[ebp+FFFFFF40h]  
002F13B2  mov         ecx,30h  
002F13B7  mov         eax,0CCCCCCCCh  
002F13BC  rep stos    dword ptr es:[edi]  
	//do nothing here
}
002F13BE  pop         edi  
002F13BF  pop         esi  
002F13C0  pop         ebx  
002F13C1  mov         esp,ebp  
002F13C3  pop         ebp  
002F13C4  ret  
--- 无源文件 -----------------------------------------------------------------------
002F13C5  int         3  
002F13C6  int         3  
002F13C7  int         3  
002F13C8  int         3  
002F13C9  int         3  
002F13CA  int         3  
002F13CB  int         3  
002F13CC  int         3  
002F13CD  int         3  
002F13CE  int         3  
002F13CF  int         3  
--- f:\cpp\clr\consoleapplication1\源.cpp ---------------------------------------


int main(int argc,char *argv[])
{
002F13D0  push        ebp  
002F13D1  mov         ebp,esp  
002F13D3  sub         esp,0C0h  
002F13D9  push        ebx  
002F13DA  push        esi  
002F13DB  push        edi  
002F13DC  lea         edi,[ebp+FFFFFF40h]  
002F13E2  mov         ecx,30h  
002F13E7  mov         eax,0CCCCCCCCh  
002F13EC  rep stos    dword ptr es:[edi]  
	//call func
	func();
//这里就编译出一条指令 call 002F11D1 这个地址是什么呢,在上面比较远的地方,为了简化,我只取一部分汇编码:
##################################
_GetLastError@0:
002F11BD  jmp         002F3972  
_RTC_GetErrorFunc:
002F11C2  jmp         002F1980  
_wcscpy_s:
002F11C7  jmp         002F391E  
@_RTC_AllocaHelper@12:
002F11CC  jmp         002F1410  
func:
002F11D1  jmp         002F13A0  ###002F11D1在这里
@_RTC_CheckStackVars@8:
002F11D6  jmp         002F1820  
@_RTC_CheckStackVars2@12:
002F11DB  jmp         002F39D0  
__RTC_CheckEsp:
##################################
//显然上面call的是一个跳转表里面的func的调转地址,而不是直接call 002F13A0(真正地址)

002F13EE  call        002F11D1  
	return 0;
002F13F3  xor         eax,eax  
}
002F13F5  pop         edi  
002F13F6  pop         esi  
002F13F7  pop         ebx  
002F13F8  add         esp,0C0h  
002F13FE  cmp         ebp,esp  
002F1400  call        002F11E0  
002F1405  mov         esp,ebp  
002F1407  pop         ebp  
002F1408  ret


C++编译时,把函数地址放到一个跳转表里面,调用函数时,去找跳转表对应函数地址,然后再jmp一次。

可以看到,调用函数需要跳两次,同时,会产生许多指令进行栈操作,不管怎样,调用一个函数必须付出192字节(本例中)代价,不管你有没有使用局部变量。所以尽量避免调用过多无意义的函数。


转载于:https://my.oschina.net/ybusad/blog/147137

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
C++ 中,虚函数是通过虚函数表来实现的。每个对象都有一个指向虚函数表的指针,虚函数表是一个数组,存储了该对象的虚函数的地址。 当调用一个虚函数时,编译器会先查找该对象的虚函数表,然后根据虚函数的索引找到对应的函数地址,最终调用该函数。 以下是一个简单的示例,展示了虚函数的汇编代码实现: ```c++ class Base { public: virtual void foo() { printf("Base::foo()\n"); } }; class Derived : public Base { public: virtual void foo() { printf("Derived::foo()\n"); } }; int main() { Base* ptr = new Derived(); ptr->foo(); delete ptr; return 0; } ``` 对应的汇编代码如下(采用 AT&T 语法): ```asm .file "main.cpp" .section .text .globl main .p2align 4,,15 .type main, @function main: .LFB0: .cfi_startproc subq $8, %rsp movl $8, %edi call operator new(unsigned long) movq %rax, %rdi leaq .LC0(%rip), %rsi movl $1, %edx movl $0, %eax call __printf_chk movq %rax, %rdi movq %rax, -8(%rbp) movq $vtable for Derived(%rip), %rax movq (%rax), %rax movq (%rax), %rax movq -8(%rbp), %rdx movq %rdx, %rsi movq %rax, (%rsp) call *%rax leaq -8(%rbp), %rax movq (%rax), %rax movq %rax, (%rsp) call operator delete(void*) xorl %eax, %eax addq $8, %rsp .cfi_endproc .LFE0: .size main, .-main .section .rodata .align 8 .LC0: .string "Base::foo()\n" .section .rodata.cst4 .align 4 vtable for Derived: .quad 0 .quad typeinfo for Derived .quad Derived::foo() .section .note.GNU-stack,"",@progbits ``` 可以看到,在调用虚函数时,程序首先通过虚函数表找到对应的函数地址,然后通过 `call` 指令调用该函数。虚函数表的地址是通过 `vtable for Derived(%rip)` 获取的。调用完毕后,还需要调用 `operator delete` 释放内存。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值