#include<stdio.h>
int Sum(int left,int right)
{
int tmp = 0;
tmp = left + right
return tmp;
}
int main()
{
int a = 10;
int b = 20;
int rt;
rt = Sum(a,b);
printf("rt:%d\n",rt);
return 0;
}
问题:函数和函数调用过程中
1.形参开不开辟内存?如果开辟,是调用方开辟,还是被调用方开辟?
实参的传递过程中,形参开辟内存,是由调用方开辟的
2.函数调用完成,怎么回退到调用方?
3.函数调用完成,怎么接着调用方继续指令?
4.函数返回值怎么返回到调用方的?
问题:函数调动第一步做什么事?
实参传递(自右向左传递)
ret:
1.pop 下一行指令寄存器
下一行指令寄存器 = 0040109A
问题:实参的入栈顺序为什么是自右向左?
如果自右向左的入栈,是能支持可变参参数
如果自左向右的入栈,不能支持可变参参数
因为c、c++语言是能够支持可变参参数的,所以入栈顺序都是自右向左的
问题:下一行指令地址入栈的目的是什么?
主要是为了保证调用方调用被调用方函数,被调用方函数清栈完成后能沿着调用方的下一行指令继续执行
问题:开栈的过程
1.压入实参,开辟形参并赋值
2.压入下一行指令地址,方便被调用方函数处理完能沿着调用方下一行指令继续执行
3.压入调用方栈底地址,方便被调用方函数处理完成能回退到调用方
4.预留被调用方函数的活动空间并作cccc cccc的初始化
问题:清栈的过程
1.清理被调用方函数预留的活动空间
2.出栈 ebp
ebp回退到被调用方栈帧上
3.出栈 下一行指令寄存器
函数调用完成,能沿着下一行指令继续执行
4.清理形参
函数返回值(前提 : 非类类型的返回值规则)
(<=4) eax
(<=8 && >4) eax edx
(>8) 临时量带出 eax保存临时量的地址
函数返回值 不能返回局部变量的地址
#include<stdio.h>
int* getValue()
{
int tmp = 100;
return &tmp;
}
void fun()
{}
int main()
{
int *p = NULL;
p = getValue();
printf("*p:%d\n",*p);
return 0;
}
内存的回收 该内存可以被重新分配
当函数调用完成 栈帧清理 局部变量也会被清理
1.调用约定
_cdecl c标准调用约定
_stdcall window标准调用约定
_fastcall 快速调用约定
符号解析
符号引用的地方找到同名符号定义的地方
不同的调用约定
1.函数符号的生成规则
2.实参的入栈顺序 自右向左
3.形参的开辟和清理
_cdecl 形参 调用方开辟 调用方清理
_stdcall 调用方开辟 被调用方清理
_fastcall 形参 前两个 寄存器直接带入 没有形参开辟
后面 处理方式和_stdcall一样
总结
1.虚拟地址空间 高
2.编译链接运行原理 高
3.函数堆栈调用 低
4.函数返回值 + 类类型返回值 中
1.虚拟地址空间 高
2.编译链接运行原理 高
3.函数堆栈调用 低
4.函数返回值 + 类类型返回值 中
5.调用约定 低