c语言中inline函数的作用

记录一下inline函数的作用

普通函数的调用过程

在eclipse里面创建工程,代码如下:

#include <iostream>
#include <vector>
#include <cstring>
#include <cassert>
#include <algorithm>
using namespace std;

//__attribute__((always_inline)) inline int test(int x)
int test(int x)
{
	return x++;
}

int main()
{
	int i = 0;
	int y = 0;

	for (i = 0; i < 9; i++)
		y = test(i);

}

上述例子,test为普通函数,单步执行时的汇编代码如下所示:

                  main():
0000000000401550:   push    %rbp
0000000000401551:   mov     %rsp,%rbp
0000000000401554:   sub     $0x30,%rsp
0000000000401558:   callq   0x4016d0 <__main>
24                	int i = 0;
000000000040155d:   movl    $0x0,-0x4(%rbp)
25                	int y = 0;
0000000000401564:   movl    $0x0,-0x8(%rbp)
27                	for (i = 0; i < 9; i++)
000000000040156b:   movl    $0x0,-0x4(%rbp)
0000000000401572:   cmpl    $0x8,-0x4(%rbp)
0000000000401576:   jg      0x40158b <main()+59>
28                		y = test(i);
0000000000401578:   mov     -0x4(%rbp),%eax
000000000040157b:   mov     %eax,%ecx
000000000040157d:   callq   0x402cc0 <test(int)>
0000000000401582:   mov     %eax,-0x8(%rbp)
27                	for (i = 0; i < 9; i++)
0000000000401585:   addl    $0x1,-0x4(%rbp)
0000000000401589:   jmp     0x401572 <main()+34>
30                }
000000000040158b:   mov     $0x0,%eax
0000000000401590:   add     $0x30,%rsp
0000000000401594:   pop     %rbp
0000000000401595:   retq 

                  test(int):
0000000000402cc0:   push    %rbp
0000000000402cc1:   mov     %rsp,%rbp
0000000000402cc4:   mov     %ecx,0x10(%rbp)
19                	return x++;
0000000000402cc7:   mov     0x10(%rbp),%eax
0000000000402cca:   lea     0x1(%rax),%edx
0000000000402ccd:   mov     %edx,0x10(%rbp)
20                }
0000000000402cd0:   pop     %rbp
0000000000402cd1:   retq   

可以看到,main函数会调用callq 402cc0去执行test函数,执行test函数时,有完整的入栈、出栈、return过程。

inline函数的调用过程
将上述代码中test函数更改如下,起初没有加__attribute__((always_inline)),由于编译选项(-O0)和单步调试的原因,inline一直没有生效,所以强制加上__attribute__((always_inline))。

__attribute__((always_inline)) inline int test(int x)
//int test(int x)
{
	return x++;
}

具体的汇编代码执行过程如下:

                  main():
0000000000401550:   push    %rbp
0000000000401551:   mov     %rsp,%rbp
0000000000401554:   sub     $0x30,%rsp
0000000000401558:   callq   0x4016d0 <__main>
24                	int i = 0;
000000000040155d:   movl    $0x0,-0x4(%rbp)
25                	int y = 0;
0000000000401564:   movl    $0x0,-0x8(%rbp)
27                	for (i = 0; i < 9; i++)
000000000040156b:   movl    $0x0,-0x4(%rbp)
0000000000401572:   cmpl    $0x8,-0x4(%rbp)
0000000000401576:   jg      0x401590 <main()+64>
0000000000401578:   mov     -0x4(%rbp),%eax
000000000040157b:   mov     %eax,-0xc(%rbp)
19                	return x++;
000000000040157e:   mov     -0xc(%rbp),%eax
0000000000401581:   lea     0x1(%rax),%edx
0000000000401584:   mov     %edx,-0xc(%rbp)
28                		y = test(i);
0000000000401587:   mov     %eax,-0x8(%rbp)
27                	for (i = 0; i < 9; i++)
000000000040158a:   addl    $0x1,-0x4(%rbp)
000000000040158e:   jmp     0x401572 <main()+34>
30                }
0000000000401590:   mov     $0x0,%eax
0000000000401595:   add     $0x30,%rsp
0000000000401599:   pop     %rbp
000000000040159a:   retq    

可以看到,直接将test函数的return x++; 三条指令插到了main函数中,不用再去压栈、执行、出栈那样操作。从代码执行效率上来讲,后面这种方式是明显优于普通函数的执行方式的。

总结:

  • inline函数调用时,不需要繁琐的入栈、出栈过程,这一点相对于普通函数执行效率会高很多;
  • inline函数在编译时,会将函数体直接插入到调用点,这样函数的调用更加简单,但是如果调用点多的话,会增加代码体积;
  • 相比于宏,inline函数是函数,可以单步调试,宏是在预编译过程中直接替换文本;
  • 虽说inline函数执行时效率高,但是不能把所有的函数都写成inline函数,一般inline函数里面不能有循环语句,一般是把频繁调用且精简的代码写成inline函数。
上一篇:PWN保护机制详解


下一篇:BUUCTF_Re_SimpleRev