函数调用时的开销

来源:互联网 发布:耳机知乎 编辑:程序博客网 时间:2024/05/29 04:51

问题引入

在学习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
、恢复原栈帧,然后将控制权转移至调用者

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

原创粉丝点击