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变量地址中存放的值。
内存运行示意图:

C++ 子函数参数传递过程的更多相关文章
- C/C++子函数参数传递,堆栈帧、堆栈参数详解
本文转载自C/C++子函数参数传递,堆栈帧.堆栈参数详解 导语 因为参数传递和汇编语言有很大联系,之后会出现较多x86汇编代码. 该文会先讲一下x86的堆栈参数传递过程,然后再分析C/C++子函数是怎 ...
- linux X64函数参数传递过程研究
基础知识 函数传参存在两种方式,一种是通过栈,一种是通过寄存器.对于x64体系结构,如果函数参数不大于6个时,使用寄存器传参,对于函数参数大于6个的函数,前六个参数使用寄存器传递,后面的使用栈传递.参 ...
- C++ 数组长度 以及 数组名作为参数传递给函数 以及 为什么不在子函数中求数组长度
在看排序,首先是插入排序,思路理清后想用代码实现,然后问题来了: 如何求数组长度? 如果没记错,在Java中应该是有直接可用的方法的, Python中(序列)也有.len,在C/C++中,字符串倒是有 ...
- windows form参数传递过程
三.windows form参数传递过程 在Windows 程序设计中参数的传递,同样也是非常的重要的. 这里主要是通过带有参数的构造函数来实现的, 说明:Form1为主窗体,包含控件:文本框text ...
- shell 脚本之获取命令输出字符串以及函数参数传递
在ubuntu 14.04之后,所有的U盘挂载也分用户之分,最近很多操作也和U盘有关,所以就研究了一上午shell脚本函数以及字符串操作的方法. 字符串操作: 获取他的命令输出比较简单,打个简单的比方 ...
- 函数可重入问题reentrant functions(函数执行过程中可以被中断,允许多个副本)
最近经常听到这个名词,以前也听到过,不过接触更多的是“线程安全问题”,而且本人也一直理解的是两个名字的含义是一样的.今天仔细总结一下这个名词相关的概念. 引用博文:可重入函数和不可重入函数 (http ...
- VB几种函数参数传递方法,Variant,数组,Optional,ParamArray
VB几种函数参数传递方法,Variant,数组,Optional,ParamArray 一) 过程的参数被缺省为具有 Variant 数据类型. 1)ByRef按 地址传递参数在 VB 中是缺省的 按 ...
- (转)C#在父窗口中调用子窗口的过程(无法访问已释放的对象)
C#在父窗口中调用子窗口的过程: 1. 创建子窗口对象 2. 显示子窗口对象 笔者的程序中,主窗体MainFrm通过菜单调用子窗口ChildFrm.在窗体中定义了子窗口对象,然后在菜单项点击事件中 ...
- PL/SQL --> 动态SQL调用包中函数或过程
动态SQL主要是用于针对不同的条件或查询任务来生成不同的SQL语句.最常用的方法是直接使用EXECUTE IMMEDIATE来执行动态SQL语句字符串或字符串变量.但是对于系统自定义的包或用户自定的包 ...
随机推荐
- 线程 IO流 网络编程 基础总结
线程 进程---->进行中的程序 线程---->由进程创建 一个进程可以创建多个线程 并发:同一个时刻 多个任务交替执行 造成一种貌似同时进行的错觉 简单来说 单个cpu的多任务就是并发 ...
- Mysql数据库语言学习的路线
对于我们数据库的学习,不管是测试人员还是开发人员以及我们的DBA来说重点都是SQL:但是我们的SQL可以分多少类型,学习重点又是在哪里呢,本文仅仅针对测试人员来展开说明: SQL:structure ...
- CS5262设计DP转HDMI 4K60HZ +VGA 1080P方案芯片
CS5262是一款带嵌入式MCU的4通道DisplayPort1.4到HDMI2.0/VGA转换器芯片,设计用于将DP1.4信号源连接到HDMI2.0接收器.CS5262集成了DP1.4兼容接收机和H ...
- Java初学者作业——编写 Java 程序,定义 Employee 类以及 HR 类,实现 HR 为 Employee 加薪的功能。
返回本章节 返回作业目录 需求说明: 编写 Java 程序,定义 Employee 类以及 HR 类,实现 HR 为 Employee 加薪的功能. 实现思路: 定义 Employee 类,并定义属性 ...
- 你还不了解SpringSecurity吗?快来看看SpringSecurity实战总结~
SpringSecurity简介: 权限管理中的相关概念 主体 principal: 使用系统的用户或设备或从其他系统远程登录的用户等等,简单说就是谁使用系统谁就是主体. 认证 authentic ...
- cpu负载
查看cpu负载,我们经常会使用top,或者是uptime命令 但是这只能看到cpu的总体的负载情况.如果我们想看cpu每个核心的负载情况是看不到的. 所以我们可以用mpstat命令 服务器一共32核心 ...
- MySQL约束和数据类型
约束条件 约束条件就是在给字段加一些约束,使该字段存储的值更加符合我们的预期. 常用约束条件如下: UNSIGNED :无符号,值从0开始,无负数 ZEROFILL:零填充,当数据的显示长度不够的时候 ...
- java 访问 太平洋网ip接口,解决前端js 跨域访问失败问题
前端 js访问太平洋网IP接口地址,返回结果是403 服务器拒绝处理异常, 于是,想到了使用 服务器端访问,然后再将查询结果返回的前端 这是Java的测试源码,[具体的contronller端源码懒得 ...
- javascript错误类型
ECMA-262 定义了下列 7 种错误类型,简单说明如下: Error:普通异常.通常与 throw 语句和 try/catch 语句一起使用. 利用属性 name 可以声明或了 解异常的类型,利用 ...
- Go语言实战-爬取校花网图片
一.目标网站分析 爬取校花网http://www.xiaohuar.com/大学校花所有图片. 经过分析,所有图片分为四个页面,http://www.xiaohuar.com/list-1-0.htm ...