编译环境: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变量地址中存放的值。

内存运行示意图:

C++ 子函数参数传递过程的更多相关文章

  1. C/C++子函数参数传递,堆栈帧、堆栈参数详解

    本文转载自C/C++子函数参数传递,堆栈帧.堆栈参数详解 导语 因为参数传递和汇编语言有很大联系,之后会出现较多x86汇编代码. 该文会先讲一下x86的堆栈参数传递过程,然后再分析C/C++子函数是怎 ...

  2. linux X64函数参数传递过程研究

    基础知识 函数传参存在两种方式,一种是通过栈,一种是通过寄存器.对于x64体系结构,如果函数参数不大于6个时,使用寄存器传参,对于函数参数大于6个的函数,前六个参数使用寄存器传递,后面的使用栈传递.参 ...

  3. C++ 数组长度 以及 数组名作为参数传递给函数 以及 为什么不在子函数中求数组长度

    在看排序,首先是插入排序,思路理清后想用代码实现,然后问题来了: 如何求数组长度? 如果没记错,在Java中应该是有直接可用的方法的, Python中(序列)也有.len,在C/C++中,字符串倒是有 ...

  4. windows form参数传递过程

    三.windows form参数传递过程 在Windows 程序设计中参数的传递,同样也是非常的重要的. 这里主要是通过带有参数的构造函数来实现的, 说明:Form1为主窗体,包含控件:文本框text ...

  5. shell 脚本之获取命令输出字符串以及函数参数传递

    在ubuntu 14.04之后,所有的U盘挂载也分用户之分,最近很多操作也和U盘有关,所以就研究了一上午shell脚本函数以及字符串操作的方法. 字符串操作: 获取他的命令输出比较简单,打个简单的比方 ...

  6. 函数可重入问题reentrant functions(函数执行过程中可以被中断,允许多个副本)

    最近经常听到这个名词,以前也听到过,不过接触更多的是“线程安全问题”,而且本人也一直理解的是两个名字的含义是一样的.今天仔细总结一下这个名词相关的概念. 引用博文:可重入函数和不可重入函数 (http ...

  7. VB几种函数参数传递方法,Variant,数组,Optional,ParamArray

    VB几种函数参数传递方法,Variant,数组,Optional,ParamArray 一) 过程的参数被缺省为具有 Variant 数据类型. 1)ByRef按 地址传递参数在 VB 中是缺省的 按 ...

  8. (转)C#在父窗口中调用子窗口的过程(无法访问已释放的对象)

    C#在父窗口中调用子窗口的过程: 1. 创建子窗口对象 2. 显示子窗口对象   笔者的程序中,主窗体MainFrm通过菜单调用子窗口ChildFrm.在窗体中定义了子窗口对象,然后在菜单项点击事件中 ...

  9. PL/SQL --> 动态SQL调用包中函数或过程

    动态SQL主要是用于针对不同的条件或查询任务来生成不同的SQL语句.最常用的方法是直接使用EXECUTE IMMEDIATE来执行动态SQL语句字符串或字符串变量.但是对于系统自定义的包或用户自定的包 ...

随机推荐

  1. F. Geometrical Progression

    http://codeforces.com/problemset/problem/758/F F. Geometrical Progression time limit per test 4 seco ...

  2. 【LeetCode】714. Best Time to Buy and Sell Stock with Transaction Fee 解题报告(Python & C++)

    作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 动态规划 日期 题目地址:https://leetc ...

  3. 【Leetcode】718. 最长重复子数组

    最长重复子数组有一下性质 A: [1,2,3,2,1] B: [3,2,1,4,7]设横是A竖是B,有规律:若横元和竖元相等,则为1,不等为0 1 2 3 2 13 0 0 1 0 12 0 1 0 ...

  4. A Primer on Domain Adaptation Theory and Applications

    目录 概 主要内容 符号说明 Prior shift Covariate shift KMM Concept shift Subspace mapping Wasserstein distance 应 ...

  5. Python Revisited Day 08 (高级程序设计技术)

    目录 8.1 过程型程序设计进阶 8.1.1 使用字典进行分支 8.1.2 生成器表达式与函数 8.1.3 动态代码执行与动态导入 动态程序设计与内省函数(表) 动态代码执行 eval(), exec ...

  6. javaScript系列 [09]-javaScript和JSON (拓展)

    本文输出JSON搜索和JSON转换相关的内容,是对前两篇文章的补充. JSON搜索 在特定的开发场景中,如果服务器端返回的JSON数据异常复杂(可能超过上万行),那么必然就有对JSON文档进行搜索的需 ...

  7. Java初学者作业——编写程序计算实发工资(实践1)

    返回本章节 返回作业目录 需求说明: 腾讯为Java工程师提供了基本工资(8000元).物价津贴及房租津贴.其中物价津贴为基本工资的40%,房屋津贴为基本工资的25%.要求编写程序计算实发工资. 实现 ...

  8. SpringBoot中如何优雅的使用多线程

    SpringBoot中如何优雅的使用多线程 当异步方法有返回值时,如何获取异步方法执行的返回结果呢?这时需要异步调用的方法带有返回值CompletableFuture

  9. websocket在线测试工具

    为了测试websocket, 根据网上的一些工具修改了一些, 因此得到了这个工具 源码 源码: <!DOCTYPE html> <html lang="en"&g ...

  10. ModelForm has no model class specified

    未指定模型类,错误发生在把model拼写错误 来自为知笔记(Wiz)