函数调用时的开销

本文探讨了函数调用时的开销,通过对比无函数调用与含有函数调用的C语言代码的反汇编,指出开销主要体现在参数压栈、控制权转移和栈帧建立等方面。虽然函数调用有成本,但在适当情况下仍应优先考虑函数的使用,只有在小规模且频繁调用的函数中才考虑用宏替换。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

问题引入

在学习C语言时,老师强调过调用函数时会有开销,但是函数调用的开销体现在哪几个方面并不十分清楚!!

举例说明

写一个两数求和的代码,此代码中不调用函数

#include <stdio.h>
int main()
{
    int a = 10, b = 20, c = 0;
    c = a + b;
    printf("%d\n", c);
    return 0;
}

该程序对应的反汇编如下:

#include <stdio.h>
int main()
{
00BA13C0  push        ebp  
00BA13C1  mov         ebp,esp  
00BA13C3  sub         esp,0E4h  
00BA13C9  push        ebx  
00BA13CA  push        esi  
00BA13CB  push        edi  
00BA13CC  lea         edi,[ebp-0E4h]  
00BA13D2  mov         ecx,39h  
00BA13D7  mov         eax,0CCCCCCCCh  
00BA13DC  rep stos    dword ptr es:[edi]  
    int a = 10, b = 20, c = 0;
00BA13DE  mov         dword ptr [a],0Ah  
00BA13E5  mov         dword ptr [b],14h  
00BA13EC  mov         dword ptr [c],0  
    c = a + b;
00BA13F3  mov         eax,dword ptr [a]  
00BA13F6  add         eax,dword ptr [b]  
00BA13F9  mov         dword ptr [c],eax  
    printf("%d\n", c);
00BA13FC  mov         esi,esp  
00BA13FE  mov         eax,dword ptr [c]  
00BA1401  push        eax  
00BA1402  push        0BA5858h  
00BA1407  call        dword ptr ds:[0BA9114h]  
00BA140D  add         esp,8  
00BA1410  cmp         esi,esp  
00BA1412  call        __RTC_CheckEsp (0BA1136h)  
    return 0;
00BA1417  xor         eax,eax  
}
00BA1419  pop         edi  
00BA141A  pop         esi  
00BA141B  pop         ebx  
00BA141C  add         esp,0E4h  
00BA1422  cmp         ebp,esp  
00BA1424  call        __RTC_CheckEsp (0BA1136h)  
00BA1429  mov         esp,ebp  
00BA142B  pop         ebp  
00BA142C  ret  

再写一个同样功能的程序,与之前不同的是该函数中有函数调用

#include <stdio.h>
int ADD(int x, int y)
{
    return x + y;
}
int main()
{
    int a = 10, b = 20, c = 0;
    c = ADD(a, b);
    printf("%d\n", c);
    return 0;
}

该函数对应的反汇编如下所示:

int main()
{
00051A00  push        ebp  
00051A01  mov         ebp,esp  
00051A03  sub         esp,0E4h  
00051A09  push        ebx  
00051A0A  push        esi  
00051A0B  push        edi  
int main()
{
00051A0C  lea         edi,[ebp-0E4h]  
00051A12  mov         ecx,39h  
00051A17  mov         eax,0CCCCCCCCh  
00051A1C  rep stos    dword ptr es:[edi]  
    int a = 10, b = 20, c = 0;
00051A1E  mov         dword ptr [a],0Ah  
00051A25  mov         dword ptr [b],14h  
00051A2C  mov         dword ptr [c],0  
    c = ADD(a, b);
00051A33  mov         eax,dword ptr [b]  
00051A36  push        eax  
00051A37  mov         ecx,dword ptr [a]  
00051A3A  push        ecx  
00051A3B  call        ADD (0511DBh)  
00051A40  add         esp,8  
00051A43  mov         dword ptr [c],eax  
    printf("%d\n", c);
00051A46  mov         esi,esp  
00051A48  mov         eax,dword ptr [c]  
00051A4B  push        eax  
00051A4C  push        55858h  
00051A51  call        dword ptr ds:[59114h]  
00051A57  add         esp,8  
00051A5A  cmp         esi,esp  
00051A5C  call        __RTC_CheckEsp (051136h)  
    return 0;
00051A61  xor         eax,eax  
}
00051A63  pop         edi  
00051A64  pop         esi  
00051A65  pop         ebx  
00051A66  add         esp,0E4h  
00051A6C  cmp         ebp,esp  
00051A6E  call        __RTC_CheckEsp (051136h)  
00051A73  mov         esp,ebp  
00051A75  pop         ebp  
00051A76  ret  

对比以上两段代码的反汇编,可以发现有如下不同
没有调用函数

c = a + b;
00BA13F3  mov         eax,dword ptr [a]  
00BA13F6  add         eax,dword ptr [b]  
00BA13F9  mov         dword ptr [c],eax  

调用函数

c = ADD(a, b);
00051A33  mov         eax,dword ptr [b]  
00051A36  push        eax  
00051A37  mov         ecx,dword ptr [a]  
00051A3A  push        ecx  
00051A3B  call        ADD (0511DBh)  
00051A40  add         esp,8  
00051A43  mov         dword ptr [c],eax  

由以上区别可知函数调用的开销在于参数的压栈过程push、和函数的调用call。

几点说明

由于我测试的环境是VS2013,很可能是编译器对程序的运行过程进行了优化,一般来说函数的开销有以下几个方面:
1、将参数压入栈中,参数越多,开销越大
2
、将控制权转移至函数中
3
、建立新的栈帧,也就是当前函数使用的“一片”栈空间,使用ebp的值来标识新的栈帧,因此要将原栈帧首地址保存下来,方便回到原来的即调用者的栈帧
4
、恢复原栈帧,然后将控制权转移至调用者

函数虽然有一定开销,但是在该使用函数的时候还是要使用函数,只用当某个函数规模较小并且调用的次数比较频繁时,就将该函数用宏代替。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值