网站代理合作,wordpress添加微信微博等小工具,美度手表网站,wordpress资源下载站前期学习的时候我们可能会有很多困惑#xff0c;如#xff1a;
#xff08;1#xff09;局部变量的值是随机值#xff1f;
#xff08;2#xff09;为什么局部变量的值是随机值#xff1f;
#xff08;3#xff09;函数是怎么传参的#xff1f;传参的顺序是怎样的… 前期学习的时候我们可能会有很多困惑如
1局部变量的值是随机值
2为什么局部变量的值是随机值
3函数是怎么传参的传参的顺序是怎样的
4形参和实参是什么关系
5函数调用是怎么做的
6函数调用结束后是怎么返回的
我们接下来使用VS2013的环境因为编译器越高级函数栈帧的过程和封装会更不容易去观察同时在不同的编译器下函数调用过程中栈帧的创建是略有差异的具体细节取决于编译器的实现。
寄存器 函数栈帧
ebpesp这2个寄存器中存放的是地址这2个地址是用来维护函数栈帧的。
每一个函数调用都要在栈区创建一个空间。 使用调用堆栈可以看到main函数被调用了但是有一个比较困惑的点main函数被谁调用了
等程序走完可以发现--tmainCRTStartup函数调用了main函数。 --tmainCRTStartup( )函数调用main函数
mainCRTStartup( )函数调用--tmainCRTStartup( )函数
每次main函数都有return0这个返回值返回到了mainret中去了。
在VS2013中main函数也是被其他函数调用的
--tmainCRTStartup( )
mainCRTStartup( ) 打开反汇编我们可以看到这个程序的汇编代码方便接下来的理解分析。 等类似的汇编代码。
接下来我们按照顺序来依次分析
push ebp 就是把ebp的值放到栈顶上面压栈 mov ebpesp
这一步就是把esp的值赋给ebp sub esp0E4h
这一步就是esp - 0E4h8进制数字十进制为228 push ebx
push esi
push edi
这三个push就是分别把ebx、esi、edi压栈。 lea edi[ebp-24h]
这个意思是把[ebp-24h]的地址放到edi里
mov ecx9
把9的数据放到ecx中
mov eax0CCCCCCCCh
把0CCCCCCCCh的数据放到eax中
rep stos dword ptr es[edi]
把从edi开始向下39h次这么多的双字节的数据空间全部都改成eax这样0CCCCCCCCh的内容。
这里强调一下
压栈就是在栈顶放一个数据进去push
出栈就是在栈顶拿走一个数据pop int a 10
mov dword ptr [ebp-8]0Ah
0A就是10就是把10放到 [ebp-8]的位置。 int b 20
mov dword ptr [ebp-14h]14h
把20放到[ebp-14]的位置上 int c 0
mov dword ptr [ebp-20h]
把0放到[edp-20h]的位置上。 c Addab
mov eaxdword ptr [edp-14h]
把[edp-14]这个位置上的值放到eax中去。 push eax
eax压栈 mov ecx dword ptr [edp-8]
把[edp-8]位置上的值放到ecx中去 push ecx
ecx压栈 当遇见call时我们使用F11进入Add函数中 在进入Add函数之前我们发现 call后面有这样一串编码栈区情况如下 这个编码是call指令的下一条指令的地址。
进入Add函数
push edp
edp压栈。 这里注意edp压栈这里存放的数据是edp指针指向的main函数栈底的地址。
mov edpesp
把esp位置的值赋给edp此时edp指针指向esp指针指向的位置。
sub esp0CCh
就是[esp-0CCh]
我们来看这个过程
mov ebpesp
sub esp0CCh
这个过程中先把esp的值赋给ebp使ebp上移至当前esp指向的位置然后将esp减去0CCh这个值使得esp向小地址移动。 我们可以发现这个过程前面为main函数创建函数栈帧的过程很像其实这个过程就是在为Add函数创建函数栈帧。 push ebx
push esi
push edi
这三步操作分别将ebx、esi、edi压栈。 lea edi[edp-0Ch]
mov ecx33h
mov eax0CCCCCCCCCCh
rep dword ptr es[edi]
这几步指令执行的是一下操作前面有说过类似的指令这里就不再重复了 这里我们来看一下之前创建Add函数的代码
int Add(int x,int y)
{
int z 0;
z x y;
return z;
} int z 0;
mov dword ptr [edp-8]0
把0放到[edp-8]的位置 z x y;
mov eax,dword ptr [ebp - 8]
这个里面放的10
把[ebp 8]里面放的值赋给eax add eax,dword ptr [ebp 0Ch]
这个里面放的20
把[ebp 0Ch]里面放的值加到eax里这时eax里面放的值为30 mov dword ptr [edp - 8],eax
把eax里的值放到[ebp - 8]中去 这一步结束后我们回想一下
在创建Add函数中根本就没有创建形参而是在main函数中将x、y的值压栈压出来然后在创建Add后引用了压栈压出来的值并且Addint x,int y中先y后x从右到左。 return z
mov eax,dword ptr [ebp - 8]
把[ebp - 8]里的值放到eax中去eax是一个寄存器在程序退出后eax寄存器会销毁。 pop edi 一次pop esp会加加并且弹出栈顶
把esp所在的栈顶弹出换成edi然后esp加加指向地址高的地方后面几步pop操作同理 pop esi
pop ebx mov esp,ebp
把ebp中的值赋给esp
执行完此步骤后 pop ebp
把栈顶上的东西弹掉并把栈顶中存放的数据赋值给ebp由于在前面创建main函数时ebp在main函数的栈底ebp压栈时将ebp当时的值压到这里的所以ebp赋到这个值后会回到main函数的栈底。 ret
将程序从子程序中返回到003710B9这个程序同时esp加加
这时就回到了call指令 call add esp8
esp加8 mov dword ptr [edp - 20h],eax
把eax中的值 (出Add函数返回的z的值) 放到[ebp - 20h]中去。 如此我们可以回答下列问题
局部变量是怎么创建的
首先函数分配好栈帧空间然后在函数的栈帧空间中给局部变量分配一部分空间。
为什么局部变量的值时随机值
因为随机值是我们放进去的CCCCCCCCC给局部变量赋值后才会见这个随机值覆盖。
函数是怎么传参的传参的顺序是怎样的
当要去调用函数之前就已经用push将参数从右到左压栈真正进入要调用的函数中通过指针的偏移量找到形参
形参和实参是什么关系
形参确实是在压栈时开辟的空间和实参在值上是相同的单实参是独立的所以形参是实参的一份临时拷贝改变形参不会改变实参。 感谢各位大佬的观看
歪比巴卜