内联函数和普通函数的反汇编对比
首先我们再viso studio中编写如下代码:
int add(int a, int b)
{
int c = a + b;
return c;
}
int main()
{
int d;
d = add(1,2);
printf("d=%d\r\n",d);
}
反汇编后得到如下汇编代码:
int add(int a, int b)
{
//入栈保护现场
00B117A0 push ebp
00B117A1 mov ebp,esp
00B117A3 sub esp,0CCh
00B117A9 push ebx
00B117AA push esi
00B117AB push edi
00B117AC lea edi,[ebp-0CCh]
00B117B2 mov ecx,33h
00B117B7 mov eax,0CCCCCCCCh
00B117BC rep stos dword ptr es:[edi]
00B117BE mov ecx,offset _549F54B0_consoleapplication1@cpp (0B1C008h)
00B117C3 call @__CheckForDebuggerJustMyCode@4 (0B1120Dh)
int c = a + b;
00B117C8 mov eax,dword ptr [a]
00B117CB add eax,dword ptr [b]
int c = a + b;
00B117CE mov dword ptr [c],eax
return c;
00B117D1 mov eax,dword ptr [c]
}
//add调用结束,出栈恢复现场
00B117D4 pop edi
00B117D5 pop esi
00B117D6 pop ebx
00B117D7 add esp,0CCh //回复sp指针,释放局部变量占用的内存
00B117DD cmp ebp,esp
00B117DF call __RTC_CheckEsp (0B11217h)
00B117E4 mov esp,ebp
00B117E6 pop ebp
00B117E7 ret
int main()
{
00CC1930 push ebp //基址指针寄存器
00CC1931 mov ebp,esp //保存当前堆栈,将栈顶指针存入ebp,当前栈顶和栈低重合
00CC1933 sub esp,0CCh //栈顶指针向下偏移0xccbyte,开辟临时变量的空间(栈空间)
00CC1939 push ebx
00CC193A push esi
00CC193B push edi
00CC193C lea edi,[ebp-0CCh]
00CC1942 mov ecx,33h
00CC1947 mov eax,0CCCCCCCCh
00CC194C rep stos dword ptr es:[edi]
00CC194E mov ecx,offset _549F54B0_consoleapplication1@cpp (0CCC008h)
00CC1953 call @__CheckForDebuggerJustMyCode@4 (0CC1221h)
int d;
d = add(1,2);
//从这里可以看出来,函数参数的入栈方向是自有向左
00CC1958 push 2 //最右边的形参入栈
00CC195A push 1 //最左边的形参入栈
00CC195C call add (0CC1186h)
00CC1961 add esp,8
00CC1964 mov dword ptr [d],eax
printf("d=%d\r\n",d);
00CC1967 mov eax,dword ptr [d]
00CC196A push eax
00CC196B push offset string "d=%d\r\n" (0CC7B30h)
00CC1970 call _printf (0CC104Bh)
00CC1975 add esp,8
}
//main函数调用结束,恢复现场
00CC1978 xor eax,eax
00CC197A pop edi
00CC197B pop esi
00CC197C pop ebx
00CC197D add esp,0CCh
00CC1983 cmp ebp,esp
00CC1985 call __RTC_CheckEsp (0CC122Bh)
00CC198A mov esp,ebp
00CC198C pop ebp
00CC198D ret
内联函数的反汇编如下:
int inline add(int a, int b)
{
int c = a + b;
return c;
}
int main()
{
000D4710 push ebp
000D4711 mov ebp,esp
000D4713 sub esp,0F8h
000D4719 push ebx
000D471A push esi
000D471B push edi
000D471C lea edi,[ebp-0F8h]
000D4722 mov ecx,3Eh
000D4727 mov eax,0CCCCCCCCh
000D472C rep stos dword ptr es:[edi]
000D472E mov eax,dword ptr [__security_cookie (0DA004h)]
000D4733 xor eax,ebp
000D4735 mov dword ptr [ebp-4],eax
000D4738 mov ecx,offset _549F54B0_consoleapplication1@cpp (0DC008h)
000D473D call @__CheckForDebuggerJustMyCode@4 (0D1212h)
char aa[20] = "123";
000D4742 mov eax,dword ptr [string "123" (0D7B30h)]
000D4747 mov dword ptr [aa],eax
000D474A xor eax,eax
000D474C mov dword ptr [ebp-18h],eax
000D474F mov dword ptr [ebp-14h],eax
000D4752 mov dword ptr [ebp-10h],eax
000D4755 mov dword ptr [ebp-0Ch],eax
int d=1;
000D4758 mov dword ptr [d],1
d = add(1,2);
//此处并没有调用add函数,而是直接把add函数展开
000D475F mov eax,1
000D4764 add eax,2
000D4767 mov dword ptr [ebp-34h],eax
000D476A mov ecx,dword ptr [ebp-34h]
000D476D mov dword ptr [d],ecx
//printf("d=%d\r\n",d);
}
000D4770 xor eax,eax
000D4772 push edx
000D4773 mov ecx,ebp
000D4775 push eax
000D4776 lea edx,ds:[0D47A4h]
000D477C call @_RTC_CheckStackVars@8 (0D123Fh)
000D4781 pop eax
000D4782 pop edx
000D4783 pop edi
000D4784 pop esi
000D4785 pop ebx
000D4786 mov ecx,dword ptr [ebp-4]
//printf("d=%d\r\n",d);
}
000D4789 xor ecx,ebp
000D478B call @__security_check_cookie@4 (0D11EAh)
000D4790 add esp,0F8h
000D4796 cmp ebp,esp
000D4798 call __RTC_CheckEsp (0D121Ch)
000D479D mov esp,ebp
000D479F pop ebp
000D47A0 ret
000D47A1 nop dword ptr [eax]
000D47A4 add dword ptr [eax],eax
000D47A6 add byte ptr [eax],al
000D47A8 lods byte ptr [esi]
000D47A9 inc edi
000D47AA or eax,0FFFFE400h
000D47AF call dword ptr [eax+eax]
000D47B2 add byte ptr [eax],al
000D47B4 mov eax,61000D47h
000D47B9 popad
000D47BA add ah,cl
对比上面两份反汇编代码可以看出,编译器在处理内联函数时会直接将其展开到调用的函数中,比普通函数少了一个入栈和出栈的过程。
内联函数的优缺点
系统在运行时,会将内联函数直接展开,而不会进行入栈和出栈操作。这样做的好处是可以节省函数调用带来的时间和空间上的开销,提高代码的执行效率,坏处是会使代码的体积庞大。
内联函数和宏定义的区别和相同点
- 内联函数和宏定义都是将函数展开,它们都会占用较大的代码空间;
- 宏定义在预处理时展开,内联函数在编译时展开;
- 编译器在处理内联函数时会进行参数检查,宏定义不会;
使用内敛函数的注意事项
1、不要在递归中使用内联函数;
2、循环次数太多的函数不应使用内敛函数定义;