一、研究问题与答案
1.形参开辟内存吗?由谁开辟?
答案:开辟内存;并且由调用方开辟;
2.形参的入栈顺序
答案:从右向左入栈,(解决可变参数的问题);实参与形参匹配类型是从左向右匹配;
3.返回值由谁带出?
答案:一般由寄存器带出;
4.被调用方结束,如何知道回退到调用方栈帧上?
答案:调用方栈底指针的地址保存到被调用方栈底指针;
5.函数调用结束,如何知道应该执行下一条指令?
答案: 将下一条指令的地址进行压栈处理;
二、了解一些汇编指令
在了解函数的堆栈调用时,首先必须熟知以下汇编语句:
1.mov 做移植处理
例:mov, dword ptr[ebp(栈底指针)-4(偏移量)] (位置), 0ah(值);
2.lea 移地址
例:eax ,[ebp-4];
3.push 压栈
例:push 0ah;
4.pop 出栈 将栈顶元素拿出放到eax中;
例:pop eax(寄存器);==> eax = pop();
5.add +=
例:add eax ,0ah ;==> eax+=0ah;
6.sub -=
例: sub eax,0ah;==>eax-=0ah;
7.call 禁止相对位移调用指令;压入下一行指令地址,jmp到被调用方函数;
8.ret 返回值指令
9.寄存器:
eax ebx ecx edx 存数据
ebp 栈底指针寄存器
esp 栈顶指针寄存器
pc 下一条指令寄存器
三、函数堆栈调用的步骤
1.形参初始化;
2.压入下一行指令地址;
3.压入调用方栈底指针寄存器的值;
4.移动ebp到被调用方栈底;
5.开辟局部变量所要的栈帧并初始化为cccccccc;
了解开栈的步骤:
1、压入实参,形参初始化 自右向左
2、压入下一行指令地址:为了在退栈的时候,沿调用方跳到下一行指令中进行执行
3、压入调用方栈底地址 回退到调用方
4、调转到被调用方函数栈帧
5、被调用方开辟 活动空间并初始化为cccccccc
四、调用约定
什么是调用约定呢?调用约定约定什么?常见的调用约定有那些呢?
1、定义:
函数的调用方和被调用方对于函数如何调用需要有一个明确的约定,只用双方都遵守同样的约定,函数才能被正确的调用,这样的约定称为调用约定也叫调用惯例;
2.约定内容
- 函数符号的生成
- 形参的入栈顺序,(从右向左)
- 形参的开辟和清理方式;
3.常见的调用约定
- C标准的调用约定 --cdecl
<调用方开辟形参并清理>
<生成的函数符号为:?函数名@@YA返回值类型 参数类型 参数类型 @Z>
- Windows的调用约定 --stdcall
<形参由调用方开辟,被调用方清理>
<生成的函数符号为:?函数名@@YG返回值类型 参数类型 参数类型 @Z>
- 快速调用约定 --fastcall
<最多使用两个寄存器来带入实参,其他参数和--stdcall的处理方式一样>
<生成的函数符号为:?函数名@@YI返回值类型 参数类型 参数类型 @Z>
- 成员方法的调用约定 --thiscall