函数调用开销

做了几个实验,简单学习了解一下函数调用的开销。
程序1—没有参数的函数调用:
[cpp] view plaincopyprint?
#include <stdio.h>  
void test() 

 return; 

int main(int argc, char *argv[]) 

 test(); 
 return 0; 

 

用gcc -S得到程序1的汇编代码:

  1.         .file   "test.c"  
  2.         .text  
  3. .globl test  
  4.         .type   test, @function  
  5. test:  
  6.         pushl   %ebp  
  7.         movl    %esp, %ebp  
  8.         nop  
  9.         popl    %ebp  
  10.         ret  
  11.         .size   test, .-test  
  12. .globl main  
  13.         .type   main, @function  
  14. main:  
  15.         pushl   %ebp  
  16.         movl    %esp, %ebp  
  17.         call    test  
  18.         movl    $0, %eax  
  19.         popl    %ebp  
  20.         ret  
  21.         .size   main, .-main  
  22.         .ident  "GCC: (GNU) 4.5.1 20100924 (Red Hat 4.5.1-4)"  
  23.         .section        .note.GNU-stack,"",@progbits  
 

从上面汇编代码可以看出,对于没有参数函数调用的开销:
1. 调用函数和返回,所以需要执行一次call/ret指令对。
2. 函数内部,需要保护现场,所以需要把%ebp push到栈中,返回前,再pop出来。
3. 构造函数运行环境,将%ebp赋值为当前的栈顶%esp。
则没有参数函数调用开销是5个指令。
程序2-带参数函数调用:

  1. #include <stdio.h>   
  2. void test(int a)  
  3. {  
  4.         (a)++;  
  5.         return;  
  6. }  
  7. int main(int argc, char *argv[])  
  8. {  
  9.         int a = 1;  
  10.         test(a);  
  11.         return 0;  
  12. }  
 

用gcc -S得到程序2的汇编代码:

  1.         .file   "test.c"  
  2.         .text  
  3. .globl test  
  4.         .type   test, @function  
  5. test:  
  6.         pushl   %ebp  
  7.         movl    %esp, %ebp  
  8.         addl    $1, 8(%ebp)  
  9.         nop  
  10.         popl    %ebp  
  11.         ret  
  12.         .size   test, .-test  
  13. .globl main  
  14.         .type   main, @function  
  15. main:  
  16.         pushl   %ebp  
  17.         movl    %esp, %ebp  
  18.         subl    $20, %esp  
  19.         movl    $1, -4(%ebp)  
  20.         movl    -4(%ebp), %eax  
  21.         movl    %eax, (%esp)  
  22.         call    test  
  23.         movl    $0, %eax  
  24.         leave  
  25.         ret  
  26.         .size   main, .-main  
  27.         .ident  "GCC: (GNU) 4.5.1 20100924 (Red Hat 4.5.1-4)"  
  28.         .section        .note.GNU-stack,"",@progbits  
 

相比于没有参数函数调用的开销,带参数函数调用多2个指令,用于传递参数:
movl -4(%ebp), %eax
movl %eax, (%ebp)
每个参数的传递时都需要2个指令。
而如果是指针参数,则函数在使用时,还得需要2个指令。
这么看,函数调用的开销还挺大的。
所以,当一个函数很小且调用频繁时,应该用宏或内联函数进行替代。
另外,虽然函数调用有开销,但除非有特殊的必要,该用函数的地方还是应该使用函数,否则会严重降低代码的可读性和可维护性。

原文地址:https://www.cnblogs.com/byfei/p/14104513.html