先抛出几个问题:
如果您对上述问题有些困惑,请继续往下看吧!
如图:
高地址的一部分空间会分配给内核,称为内核空间,剩下的内存空间给用户使用,称为用户空间。
用户空间中有几个主要的内存区域:
我们都知道函数调用都是以栈帧为单位,机器通常用栈来传递函数参数、保存返回地址、保存寄存器(即函数调用的上下文)及存储本地局部变量等。
一个单独的栈帧结构如图所示:
为单个函数调用分配的那部分栈称为栈帧,栈帧的边界由两个指针界定:寄存器%ebp为帧指针,指向当前栈帧的起始处,通常较为固定;寄存器%esp为栈指针,指向当前栈帧的栈顶位置,当程序执行时,栈指针可以移动,因此大多数数据的访问都是相对于帧指针的。
一次函数调用的栈帧图如下:
直接看图:
图片来源于网络,侵权删
上图表达的应该已经很清楚啦,简单示例解释一下,函数调用需要传递参数时,第一个参数存到%edi里,第二个参数会存到%esi里,如果有返回值会存到%eax里,这里如果是64位的返回值,会使用%rax。
这里主要涉及三种约定:
C语言默认的调用约定是cdecl方式,可以通过__attribute__((cdecl))标明使用cdecl约定,其实还有其它一些调用约定,如图:
这里有几种情况:
4字节:当函数返回值是4个字节会通过%eax寄存器作为通道,函数将返回值存储在%eax中,返回后函数的调用方再读取%eax。
5-8个字节:通过rax寄存器作为通道。
大于8个字节:以如下代码举例:
struct A {
// ...大于8字节
};
A func() {
A b;
return b;
}
A x = func();
返回值传递方式如图:
返回值类型的尺寸太大导致函数返回时,会开辟一段区域作为中介,返回值对象会被拷贝两次,而C++在有些情况下会做返回值优化,减少拷贝的次数.
Copyright© 2013-2020
All Rights Reserved 京ICP备2023019179号-8