我是靠谱客的博主 无限自行车,最近开发中收集的这篇文章主要介绍C函数调用过程分析,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

本文最新地址http://exbrowser.com/?p=592

一.环境:

    x86/WinXP/VC 6.0

二.用例:
int swap(int a, int b)
{
 int v;
 v = a;
 a = b;
 b = v;
 return v;
}

void main(void)
{
 int a = 7;
 int b = 10;
 int c = 0;
 c = swap(a,b);
 return;
}

三.分析:
1:    int swap(int a, int b)
2:    {
00401020   push        ebp
00401021   mov         ebp,esp
00401023   sub         esp,44h
00401026   push        ebx
00401027   push        esi
00401028   push        edi
00401029   lea         edi,[ebp-44h]
0040102C   mov         ecx,11h
00401031   mov         eax,0CCCCCCCCh
00401036   rep stos    dword ptr [edi]
3:        int v;
4:        v = a;
00401038   mov         eax,dword ptr [ebp+8]
0040103B   mov         dword ptr [ebp-4],eax
5:        a = b;
0040103E   mov         ecx,dword ptr [ebp+0Ch]
00401041   mov         dword ptr [ebp+8],ecx
6:        b = v;
00401044   mov         edx,dword ptr [ebp-4]
00401047   mov         dword ptr [ebp+0Ch],edx
7:        return v;
0040104A   mov         eax,dword ptr [ebp-4]
8:    }
0040104D   pop         edi
0040104E   pop         esi
0040104F   pop         ebx
00401050   mov         esp,ebp
00401052   pop         ebp
00401053   ret

swap函数内部参数处理:
  1.将基址指针EBP压栈;
  2.将堆栈指针ESP拷贝一份到EBP中;
  3.将ESP值减0x44(为了将这68个字节填充为0xCC);
  4.将EBX/ESI/EDI压栈;
  5.将EBP-0x44的地址偏移量存到目标地址指针EDI;
  6.将计数器ECX置为0x11(即17个整数);
  7.将EDI所开始的大小为17的内存空间填充为0xCCCCCCCC;
  
  8.取出堆栈中被压的a的值(EBP+8所存的值)放到EAX中;
  9.将EAX中的值(参数a的值)赋给到v;
  10.取出堆栈中被压的b的值(EBP+0CH所存的值)放到ECX中;
  11.将ECX中的值(参数b的值)赋给到a;
  12.取出堆栈中被压的b的值(EBP-4所存的值)放到EDX中;
  13.将EDX中的值(变量v的值)赋给到a;
  
  14.将返回值(v的值)拷贝到EAX中;
  
  15.恢复EDI/ESI/EBX;
  16.恢复ESP为EBP(即进入swap时的ESP);
  17.恢复EBP为调用swap前的值;
  18.swap函数返回;


10:   void main(void)
11:   {
00401070   push        ebp
00401071   mov         ebp,esp
00401073   sub         esp,4Ch
00401076   push        ebx
00401077   push        esi
00401078   push        edi
00401079   lea         edi,[ebp-4Ch]
0040107C   mov         ecx,13h
00401081   mov         eax,0CCCCCCCCh
00401086   rep stos    dword ptr [edi]
12:       int a = 7;
00401088   mov         dword ptr [ebp-4],7
13:       int b = 10;
0040108F   mov         dword ptr [ebp-8],0Ah
14:       int c = 0;
00401096   mov         dword ptr [ebp-0Ch],0
15:       c = swap(a,b);
0040109D   mov         eax,dword ptr [ebp-8]
004010A0   push        eax
004010A1   mov         ecx,dword ptr [ebp-4]
004010A4   push        ecx
004010A5   call        @ILT+5(_swap) (0040100a)
004010AA   add         esp,8
004010AD   mov         dword ptr [ebp-0Ch],eax
16:       return;
17:   }
004010B0   pop         edi
004010B1   pop         esi
004010B2   pop         ebx
004010B3   add         esp,4Ch
004010B6   cmp         ebp,esp
004010B8   call        __chkesp (004010e0)
004010BD   mov         esp,ebp
004010BF   pop         ebp
004010C0   ret


15:       c = swap(a,b);
0040109D   mov         eax,dword ptr [ebp-8]
004010A0   push        eax
004010A1   mov         ecx,dword ptr [ebp-4]
004010A4   push        ecx
004010A5   call        @ILT+5(_swap) (0040100a)
004010AA   add         esp,8
004010AD   mov         dword ptr [ebp-0Ch],eax
分析函数swap调用过程:
  1.将参数值b拷到EAX;
  2.将EAX值压栈;
  3.将参数值a拷到ECX;
  4.将ECX值压栈;
  5.CALL swap函数
  6.将堆栈指针加8,丢弃堆栈中a,b的值;
  7.将EAX中的返回值拷贝到变量c;

调用swap时的堆栈情况:

  0012FEC8 | EDI        |<-- ESP (执行到 v = a语句时堆栈指针)
  0012FECC | ESI        |
  0012FED0 | EBX        |
  0012FED4 | 0xCCCCCCCC |
           |     .      |
           |     .      |
           |     .      |
  0012FF14 | 0xCCCCCCCC |
  0012FF18 | EBP        |<-- EBP (执行到 v = a语句时堆栈指针)
  0012FF1C | 0x004010AA | 执行完调用swap后的返回地址
  0012FF20 | ECX        | a的值
  0012FF24 | EAX        | b的值


四.总结:

1.实参压栈过程是从右至左,然后压入函数调用后的返回地址;
2.被调用的函数从堆栈中取实参值;
3.如果采用编译器进行优化,那么被调用的函数内部是从寄存器还是从堆栈中取实参值?具体情况根据CPU架构和参数个数有关(可以继续分析哦!);
4.如果函数参数个数超过较多(如果超过5个参数),函数参数如何传递呢?
5.函数的返回值一般会存在一个寄存器,如EAX.

最后

以上就是无限自行车为你收集整理的C函数调用过程分析的全部内容,希望文章能够帮你解决C函数调用过程分析所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(39)

评论列表共有 0 条评论

立即
投稿
返回
顶部