熟悉掌握c++中函数调用堆栈,内存管理,智能指针
c++如何在程序运行时为程序调用内核堆栈的呢?
CPU是如何识别被调参数的呢?怎么保存传递的参数的呢?
所以cpu引进了栈的数据结构来完成函数的调用,函数调用时依次把参数压栈,然后调用函数,
调用完以后从堆栈中取出数据并计算,计算结束后清栈
回想这个时先回想一下虚拟空间的空间布局和调用约定
int sum(int a,int b) { int tmp=0; tmp=a+b; return tmp; } int main() { int a=10; int b=20; int ret=0; ret=sum(a,b); Printf(“%d”,ret); } |
虚拟空间内存布局:1G内核空间,3G用户空间
保留区 128M 不可读不可写 |
0x00000000 -- 0x0804 8000 |
.text 指令 |
|
.data |
|
.bss未初始化 |
(执行前清0) |
heap 堆 |
向下开辟 |
共享库 |
|
stack 栈 |
向上开辟 |
环境变量 |
0xc000 0000 |
DMA |
|
NORMAL |
|
HIGHMEM |
0xffff ffff |
函数调用的具体情况:
Push 压栈,pop 出栈,eax,ebx:存放数值
ebp:栈底指针寄存器esp:栈顶指针寄存器call:压入下一行的指令地址
返回值:4字节 eax带出,8 eax edx 》8 用临时量带回(在调用方栈帧开辟,临时量在形参压栈后压栈)
具体实现如下:
0x0000 0000 esp |
tmp |
main函数的栈底指针 ebp |
下一条指令的地址 (&&printf) |
eax 10 |
eax 20 |
X X X X X X X X X X |
ret 0 |
b 10 |
a 20 |
mainCRTstartup函数的栈底指针 |
关于四种调用约定:
参数的传递中有两个问题需要明确 约定符号的生成,形参的入栈顺序(pasclcall),形参的开辟和清理方式
_cdecl:c标准调用约定 调用方开辟,调用方清除 如上
_stdcall:windows标准调用约定 自右向左入栈 调用方开辟,被调用方清除
_fastcall:快速调用约定 形参不开辟 在寄存器中存放 若形参个数过多的情况则
1)形参字节小于等于4 前两个形参不开辟内存寄存器带入被调用方 第三个开始和_stdcall调用约定相同
2)形参字节数大于4 和——stdcall相同,调用方开辟,被调用方清理
_thiscall:c++成员方法的调用约定 ,对参数个数不定的 调用者清栈,否则函数自己清栈
//汇编太麻烦了,你们可以自己去看看
c++是如何管理内存的呢?
大家都知道c语言在管理内存时候的方式:即上图;其实c++的内存管理也是如此
那就说一下c++的几种存储结构吧(堆,栈,全局/静态存储区,文字常量区,text段)
栈:局部变量,参数,编译器自动开辟释放,相当于计算机系统的数据结构分配专门的寄存器来存储栈的地址,所以效率较高,内存空间连续,但由于向下开辟,空间有限
堆:手动开辟释放,向上开辟,内存空间无限制,内存不连续,会有内存碎片产生,具体的new和malloc以后会具体说明
全局/静态存储区:全局变量,静态变量,程序结束时自动释放,包括data段和bass段
文字常量区:存放在data段
text段:存放程序的二进制代码