C++ Basics - Inline Functions

A function modified with inline is called an inline function. When compiling , the C++ compiler will expand the place where the inline function is called . There is no overhead of function stacking. Inline functions improve the efficiency of program operation.

Note: Inlining just defines the function as inlining, which is equivalent to giving the compiler an option. Whether the compiler uses inlining is completely up to the compiler itself. In general, only functions that meet certain requirements will use inlining 

We observe the underlying implementation of inline functions and non-inline functions by looking at the form of disassembly. Suppose we implement an addition function Add(int int) as follows

int Add(int left, int right)
{
    return left + right;
}

int main()
{
    int a = 10;
    int b = 20;

    int ret = Add(a, b);

    return 0;
}

At this time, the inline keyword is not added, use g++ to compile this code and use objdump to view the disassembly to get the assembly as

0000000000400521 <main>:
  400521:	55                   	push   %rbp             // %rbp入栈,保存上一个栈帧的栈底地址
  400522:	48 89 e5             	mov    %rsp,%rbp        // %rsp值给%rbp,%rbp保存当前栈帧栈底地址
  400525:	48 83 ec 10          	sub    $0x10,%rsp       // %rsp-0x10,为main函数开辟16个字节栈帧
  400529:	c7 45 fc 0a 00 00 00 	movl   $0xa,-0x4(%rbp)  // 将10 保存在栈底-4地址处
  400530:	c7 45 f8 14 00 00 00 	movl   $0x14,-0x8(%rbp) // 将20 保存在占地-8位置处
  400537:	8b 55 f8             	mov    -0x8(%rbp),%edx  // 将20 保存到寄存器%edx内
  40053a:	8b 45 fc             	mov    -0x4(%rbp),%eax  // 将10 保存到寄存器%eax内
  40053d:	89 d6                	mov    %edx,%esi        // 将20 传递到寄存器%esi内作为第二个参数
  40053f:	89 c7                	mov    %eax,%edi        // 将10 传递到寄存器%edi内作为第一个参数
  400541:	e8 c7 ff ff ff       	callq  40050d <_Z3Addii>// call函数Add实现函数调用
  400546:	89 45 f4             	mov    %eax,-0xc(%rbp)  // 将%eax中的结果(由Add返回)保存到栈上(ret)
  400549:	b8 00 00 00 00       	mov    $0x0,%eax        // 将0传入%eax作为main函数返回值
  40054e:	c9                   	leaveq                  // 恢复%rsp和%rbp
  40054f:	c3                   	retq 
000000000040050d <_Z3Addii>:
  40050d:	55                   	push   %rbp            // 将main函数的栈底地址入栈
  40050e:	48 89 e5             	mov    %rsp,%rbp       // %rsp保存当前栈底
  400511:	89 7d fc             	mov    %edi,-0x4(%rbp) // 将参数一入栈
  400514:	89 75 f8             	mov    %esi,-0x8(%rbp) // 将参数二入栈
  400517:	8b 45 f8             	mov    -0x8(%rbp),%eax // 将参数二保存到寄存器%eax
  40051a:	8b 55 fc             	mov    -0x4(%rbp),%edx // 将参数一保存到寄存器%edx
  40051d:	01 d0                	add    %edx,%eax       // 完成 10 + 20,并将结果保存到%eax中
  40051f:	5d                   	pop    %rbp            // 从栈中弹出main函数栈底地址并保存到%rbp
  400520:	c3                   	retq                   // 从栈中弹出返回地址并返回main

It can be seen that for ordinary function calls, the call instruction will be used to call <_Z3Addii> located at 40050d. At this time, a stack frame will be opened for the function Add, which will bring a certain amount of overhead.

Then look at the inline version of the Add function, since g++ does not use inline by default, we need to do some processing

inline int Add(int left, int right) __attribute__((always_inline));

inline int Add(int left, int right)
{
    return left + right;
}

int main()
{
    int a = 10;
    int b = 20;

    int ret = Add(a, b);

    return 0;
}

Add __attribute__((always_inline)) after the declaration to force the g++ compiler to use inline

Also use objdump to obtain disassembly as follows, and <_Z3Addii> in the obtained disassembly no longer exists

000000000040064d <main>:
  40064d:	55                   	push   %rbp
  40064e:	48 89 e5             	mov    %rsp,%rbp
  400651:	c7 45 fc 0a 00 00 00 	movl   $0xa,-0x4(%rbp)    // 10 保存到栈上(rbp-4)a
  400658:	c7 45 f8 14 00 00 00 	movl   $0x14,-0x8(%rbp)   // 20 保存到栈上(rbp-8)b
  40065f:	8b 45 fc             	mov    -0x4(%rbp),%eax    // 10 保存到寄存器%eax内
  400662:	89 45 f0             	mov    %eax,-0x10(%rbp)   // 将10保存到栈上(rbp-16)left
  400665:	8b 45 f8             	mov    -0x8(%rbp),%eax    // 20 保存到寄存器%eax内
  400668:	89 45 ec             	mov    %eax,-0x14(%rbp)   // 将20保存到栈上(rbp-20)right
  40066b:	8b 45 ec             	mov    -0x14(%rbp),%eax   // 将20保存到寄存器%eax内
  40066e:	8b 55 f0             	mov    -0x10(%rbp),%edx   // 将10保存到寄存器%edx内
  400671:	01 d0                	add    %edx,%eax          // 10 + 20,将结果保存到%eax
  400673:	89 45 f4             	mov    %eax,-0xc(%rbp)    // 将结果保存到栈上(rbp-12)ret
  400676:	b8 00 00 00 00       	mov    $0x0,%eax          // 将0传入%eax作为main函数返回值
  40067b:	5d                   	pop    %rbp               // 恢复%rbp
  40067c:	c3                   	retq 

After using the inline function, the operation of the original function call is directly carried out in the stack frame of the current function. In fact, a copy is created for the parameters a and b on the stack. The entire function stack frame is

  •  inline is a way of exchanging space for time . If the compiler treats the function as an inline function, it will replace the function call with the function body during the compilation phase. Defect: it may make the object file larger. Advantage: less call overhead , improve the efficiency of program operation
  • Inline is just a suggestion for the compiler. Different compilers may have different inline implementation mechanisms. The general suggestion is: make the function smaller (that is, the function is not very long, there is no exact statement, it depends on the internal implementation of the compiler), no Recursive and frequently called functions should be inline modified, otherwise the compiler will ignore the inline feature
  • Inline does not recommend separation of declaration and definition (different files) , separation will cause link errors. Because inline is expanded, there is no function address, and the link will not be found
  • The keyword inline must be placed together with the function definition body to make the function inline, just putting inline in front of the function declaration has no effect

Therefore, for macro functions used in C language, it is recommended to use inline in C++ instead 

Guess you like

Origin blog.csdn.net/weixin_58165485/article/details/128576243