C++ 子函数参数传递过程

编译环境:Visual Studio 2015

参数传递与汇编语言有很大关系。子函数传递参数主要方式有三种(这三种参数传递方式都可用用于x86汇编语言甚至其它汇编语言):

  • 寄存器方式传递参数
  • 存储器方式传递参数
  • 堆栈方式传递参数

在C++编译时,编译器采用堆栈方式传递参数。

 测试代码:

int add_num(int x, int y)
{    
    x++;
    y++;
    int sum = x + y;
    cout << "add_num->x:" << (int*)&x << endl;
    cout << "add_num->y:" << (int*)&y << endl;
    return sum;
}

int main()
{    
    int sum = 0;
    int x = 3;
    cout << "main->x:" << (int*)&x<< endl;
    int y = 4;
    cout << "main->y:" << (int*)&y << endl;
    sum = add_num(x, y);
    return 0;
}

add_num()函数反汇编代码

int add_num(int x, int y)
{    
001620C0  push        ebp  
001620C1  mov         ebp,esp  
001620C3  sub         esp,0CCh  
001620C9  push        ebx  
001620CA  push        esi  
001620CB  push        edi  
001620CC  lea         edi,[ebp-0CCh]  
001620D2  mov         ecx,33h  
001620D7  mov         eax,0CCCCCCCCh  
001620DC  rep stos    dword ptr es:[edi]  
    x++;
001620DE  mov         eax,dword ptr [x]  
001620E1  add         eax,1  
001620E4  mov         dword ptr [x],eax  
    y++;
001620E7  mov         eax,dword ptr [y]  
001620EA  add         eax,1  
001620ED  mov         dword ptr [y],eax  
    int sum = x + y;
001620F0  mov         eax,dword ptr [x]  
001620F3  add         eax,dword ptr [y]  
001620F6  mov         dword ptr [sum],eax  
    cout << "add_num->x:" << (int*)&x << endl;
001620F9  mov         esi,esp  
001620FB  push        offset std::endl<char,std::char_traits<char> > (01614F1h)  
00162100  mov         edi,esp  
00162102  lea         eax,[x]  
00162105  push        eax  
00162106  push        offset string "add_num->x:" (0169B30h)  
0016210B  mov         ecx,dword ptr [_imp_?cout@std@@3V?$basic_ostream@DU?$char_traits@D@std@@@1@A (016D098h)]  
00162111  push        ecx  
00162112  call        std::operator<<<std::char_traits<char> > (0161519h)  
00162117  add         esp,8  
0016211A  mov         ecx,eax  
0016211C  call        dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (016D0A4h)]  
00162122  cmp         edi,esp  
00162124  call        __RTC_CheckEsp (0161181h)  
00162129  mov         ecx,eax  
0016212B  call        dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (016D0ACh)]  
00162131  cmp         esi,esp  
00162133  call        __RTC_CheckEsp (0161181h)  
    cout << "add_num->y:" << (int*)&y << endl;
00162138  mov         esi,esp  
0016213A  push        offset std::endl<char,std::char_traits<char> > (01614F1h)  
0016213F  mov         edi,esp  
00162141  lea         eax,[y]  
00162144  push        eax  
00162145  push        offset string "add_num->y:" (0169B3Ch)  
0016214A  mov         ecx,dword ptr [_imp_?cout@std@@3V?$basic_ostream@DU?$char_traits@D@std@@@1@A (016D098h)]  
00162150  push        ecx  
00162151  call        std::operator<<<std::char_traits<char> > (0161519h)  
00162156  add         esp,8  
00162159  mov         ecx,eax  
0016215B  call        dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (016D0A4h)]  
00162161  cmp         edi,esp  
00162163  call        __RTC_CheckEsp (0161181h)  
00162168  mov         ecx,eax  
0016216A  call        dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (016D0ACh)]  
00162170  cmp         esi,esp  
00162172  call        __RTC_CheckEsp (0161181h)  
    return sum;
00162177  mov         eax,dword ptr [sum]  //通过eax寄存器返回计算结果
}
0016217A  pop         edi  
0016217B  pop         esi  
0016217C  pop         ebx  
0016217D  add         esp,0CCh  
00162183  cmp         ebp,esp  
00162185  call        __RTC_CheckEsp (0161181h)  
0016218A  mov         esp,ebp  
0016218C  pop         ebp  
0016218D  ret  

在函数add_num内部,x地址为0x00f8fc4c,y地址为0x00f8fc50,函数ebp地址为0x00f8fc44。

main函数反汇编代码:

int main()
{    
00162270  push        ebp  
00162271  mov         ebp,esp  //保存main函数基地址0x00f8fd48
00162273  sub         esp,0E8h  //申请栈内存空间,栈顶地址为0x00f8fc60
00162279  push        ebx  
0016227A  push        esi  
0016227B  push        edi  
0016227C  lea         edi,[ebp-0E8h]  
00162282  mov         ecx,3Ah  
00162287  mov         eax,0CCCCCCCCh  
0016228C  rep stos    dword ptr es:[edi]  
0016228E  mov         eax,dword ptr [__security_cookie (016C004h)]  
00162293  xor         eax,ebp  
00162295  mov         dword ptr [ebp-4],eax//计算sum变量地址=ebp-4字节(前面有4次压栈)-4字节(默认变量间隔)-4字节(变量本身长度)=0x00f8fd48-12=0x00f8fd3c。
    int sum = 0;
00162298  mov         dword ptr [sum],0  
    int x = 3;
0016229F  mov         dword ptr [x],3  //同理 x地址= sum地址-8字节(默认变量将)-4字节(变量本身长度)=0x00f8fd30。
    cout << "main->x:" << (int*)&x<< endl;
001622A6  mov         esi,esp  
001622A8  push        offset std::endl<char,std::char_traits<char> > (01614F1h)  
001622AD  mov         edi,esp  
001622AF  lea         eax,[x]  
001622B2  push        eax  
001622B3  push        offset string "main->x:" (0169B50h)  
001622B8  mov         ecx,dword ptr [_imp_?cout@std@@3V?$basic_ostream@DU?$char_traits@D@std@@@1@A (016D098h)]  
001622BE  push        ecx  
001622BF  call        std::operator<<<std::char_traits<char> > (0161519h)  
001622C4  add         esp,8  
001622C7  mov         ecx,eax  
001622C9  call        dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (016D0A4h)]  
001622CF  cmp         edi,esp  
001622D1  call        __RTC_CheckEsp (0161181h)  
001622D6  mov         ecx,eax  
001622D8  call        dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (016D0ACh)]  
001622DE  cmp         esi,esp  
001622E0  call        __RTC_CheckEsp (0161181h)  
    int y = 4;
001622E5  mov         dword ptr [y],4  //同理,y地址= x地址-8字节-4字节 = 0x00f8fd24。sum ,x ,y地址在下图显示结果一致。
    cout << "main->y:" << (int*)&y << endl;
001622EC  mov         esi,esp  
001622EE  push        offset std::endl<char,std::char_traits<char> > (01614F1h)  
001622F3  mov         edi,esp  
001622F5  lea         eax,[y]  
001622F8  push        eax  
001622F9  push        offset string "main->y:" (0169B5Ch)  
001622FE  mov         ecx,dword ptr [_imp_?cout@std@@3V?$basic_ostream@DU?$char_traits@D@std@@@1@A (016D098h)]  
00162304  push        ecx  
00162305  call        std::operator<<<std::char_traits<char> > (0161519h)  
0016230A  add         esp,8  
0016230D  mov         ecx,eax  
0016230F  call        dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (016D0A4h)]  
00162315  cmp         edi,esp  
00162317  call        __RTC_CheckEsp (0161181h)  
0016231C  mov         ecx,eax  
0016231E  call        dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (016D0ACh)]  
00162324  cmp         esi,esp  
00162326  call        __RTC_CheckEsp (0161181h)  
    sum = add_num(x, y);
0016232B  mov         eax,dword ptr [y]  
0016232E  push        eax  //形参y压栈
0016232F  mov         ecx,dword ptr [x]  
00162332  push        ecx  //形参x压栈
00162333  call        add_num (01614E7h)  
00162338  add         esp,8  
0016233B  mov         dword ptr [sum],eax  //将add_num函数存放在eax寄存器的计算结果赋给sum变量。
    return 0;
0016233E  xor         eax,eax  
}

main()函数ebp地址为0x00f8fd48。x地址为0x00f8fd30,且x地址中放的还是调用add_num函数之前的原始值3。y地址为0x00f8fd24,且x地址中放的还是调用add_num函数之前的原始值4。sum地址为0x00f8fd3c,该地址存放的值在调用add_num函数之后由0变为9。

流程分析:

  • 变量 x,y 存放在main函数栈中。
  • 调用子函数之前,将变量x,y的值在main函数栈顶压栈(参数栈)。
  • 进入子函数后,将将当前栈顶地址作为子函数基地址,其中保存调用函数的基地址。
  • 子函数申请内存空间。并在参数栈中执行x++,y++运行,通过寄存器eax计算x+y结果,在子函数栈内计算sum地址,并保存运行结果。
  • 最后通过 eax寄存器返回子函数返回值。

结论:

  • C++编译器采用堆栈方式传递参数。
  • 子函数对参数的运算在参数栈上进行。在参数栈上的运算不会影响到变量x,y变量地址中存放的值。

内存运行示意图:

 

posted @ 2021-04-14 18:02  钟齐峰  阅读(774)  评论(0编辑  收藏  举报