做了几个实验,简单学习了解一下函数调用的开销。

程序1—没有参数的函数调用:

[cpp] view plaincopyprint?

#include

void test()

{

return;

}

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

{

test();

return 0;

}

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

[cpp]

view plaincopyprint?

.file "test.c" .text .globl test .type test, @function test: pushl %ebp movl %esp, %ebp nop popl %ebp ret .size test, .-test .globl main .type main, @function main: pushl %ebp movl %esp, %ebp call test movl $0, %eax popl %ebp ret .size main, .-main .ident "GCC: (GNU) 4.5.1 20100924 (Red Hat 4.5.1-4)" .section .note.GNU-stack,"",@progbits

从上面汇编代码可以看出,对于没有参数函数调用的开销:

1. 调用函数和返回,所以需要执行一次call/ret指令对。

2. 函数内部,需要保护现场,所以需要把%ebp push到栈中,返回前,再pop出来。

3. 构造函数运行环境,将%ebp赋值为当前的栈顶%esp。

则没有参数函数调用开销是5个指令。

程序2-带参数函数调用:

[cpp]

view plaincopyprint?

#include void test(int a) { (a)++; return; } int main(int argc, char *argv[]) { int a = 1; test(a); return 0; }

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

[cpp]

view plaincopyprint?

.file "test.c" .text .globl test .type test, @function test: pushl %ebp movl %esp, %ebp addl $1, 8(%ebp) nop popl %ebp ret .size test, .-test .globl main .type main, @function main: pushl %ebp movl %esp, %ebp subl $20, %esp movl $1, -4(%ebp) movl -4(%ebp), %eax movl %eax, (%esp) call test movl $0, %eax leave ret .size main, .-main .ident "GCC: (GNU) 4.5.1 20100924 (Red Hat 4.5.1-4)" .section .note.GNU-stack,"",@progbits

相比于没有参数函数调用的开销,带参数函数调用多2个指令,用于传递参数:

movl -4(%ebp), %eax

movl %eax, (%ebp)

每个参数的传递时都需要2个指令。

而如果是指针参数,则函数在使用时,还得需要2个指令。

这么看,函数调用的开销还挺大的。

所以,当一个函数很小且调用频繁时,应该用宏或内联函数进行替代。

另外,虽然函数调用有开销,但除非有特殊的必要,该用函数的地方还是应该使用函数,否则会严重降低代码的可读性和可维护性。