编译环境: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. 【剑指Offer】数据流中的中位数 解题报告(Python)

    [剑指Offer]数据流中的中位数 解题报告(Python) 标签(空格分隔): 剑指Offer 题目地址:https://www.nowcoder.com/ta/coding-interviews ...

  2. Spring MVC 文件上传、Restful、表单校验框架

    目录 文件上传 Restful Restful 简介 Rest 行为常用约定方式 Restful开发入门 表单校验框架 表单校验框架介绍 快速入门 多规则校验 嵌套校验 分组校验 综合案例 实用校验范 ...

  3. Challenging Common Assumptions in the Unsupervised Learning of Disentangled Representations

    目录 概 主要内容 Locatello F., Bauer S., Lucic M., R"{a}tsch G., Gelly S. Sch"{o}lkopf and Bachem ...

  4. Chapter 8 Selection Bias

    目录 8.1 The structure of selection bias 8.2 Examples of selection bias 8.3 Selection bias and confoun ...

  5. Django项目部署到Apache服务器上

    之前写了把Django部署到XAMPP上,但是有bug,翻apache日志的时候发现会无法import _ssl,然后我就怒而直接装apache2了 配置方法大约和这篇文章差不多 安装必要的包 sud ...

  6. CS5266代替AG9311|Type C转HDMI带PD3.0转换芯片|AG9311替代方案

    ALGOLTEK AG9311是一款带PD3.0 Type C转HDMI的转换芯片,它主要用于usb Type-c拓展坞以及多功能usb Type-c转换器等产品设计当中,台湾瑞奇达新推出的CS526 ...

  7. Java Web程序设计笔记 • 【第7章 会话跟踪技术】

    全部章节   >>>> 本章目录 7.1 会话跟踪技术概述 7.1.1 为什么需要会话跟踪 7.1.2 会话的状态和跟踪 7.1.3 会话跟踪技术 7.1.4 会话跟踪工作流程 ...

  8. Vue-cli3.0配置全局less

    第一种配置方式(推荐) npm install style-resources-loader vue-cli-plugin-style-resources-loader less-loader les ...

  9. (五)React Ant Design Pro + .Net5 WebApi:后端环境搭建-Autofac注入+ 泛型仓储

    一. 简介 Autofac与.Net Core自带DI的区别,大佬级的文章数不胜数.我只是根据实际应用简单介绍(非常简单的那种) 1.批量注入,自带DI需要自己写循环反射注入,Autofac现成方法, ...

  10. Salesforce LWC学习(三十九) lwc下quick action的recordId的问题和解决方案

    本篇参考: https://developer.salesforce.com/docs/component-library/bundle/force:hasRecordId/documentation ...