堆栈(stack)又称为栈或堆叠
一种计算机最常用的数据结构之一,有以下几个相关概念:
- 栈顶和栈底:允许元素插入与删除的一端称为
栈顶
,另一端称为栈底
。 - 压栈POP:栈的插入操作,叫做进栈,也称压栈、入栈。
- 弹栈PUSH:栈的删除操作,也叫做出栈。
- 栈操作原则(First In Last Out,后进先出)。
- 栈增长方向,C语言默认是从高到低,也就是逆增长
需要注意的是,栈顶和栈底容易和增长方向混淆,高到低的增长方向,那么底部是栈顶,也就是“逆”向的。
栈帧
栈操作频繁的就是函数调用,而栈帧就是每个函数独有的栈。
而一个栈中按照函数调用顺序有多个栈帧按增长方向顺序排列,不同的函数的栈帧在一个栈中的位置可以相同,也就是栈帧存在覆盖的可能性,详细的
参考
4.3.2 栈帧格式
栈相关指针
从技术上说,栈就是CPU寄存器里的某个指针所指向的一片内存区域。了解指针是必要的,而且需要知道栈指针由特定寄存器保存,不同平台命名不同,而通用的规则有ATPCS。
SP寄存器-ARM平台(x86/x64平台的ESP寄存器
/RSP寄存器
)
sp, Stack Pointer ,寄存器是栈顶指针,特点为
随着栈操作一直变化
按照栈增长方向增加机器对应位数的数值,以从高到低增长为例,入栈则指针减去4(32位)或8(64位)
BP寄存器-ARM平台(x86/x64平台的EBP寄存器
/RBP寄存器
)
bp寄存器是栈底指针,也是帧指针,指向的是栈帧的底部,特点为
栈操作栈帧指针不会变化
帧指针每次都会随函数调用是指向新的函数栈帧的底部
这里说的可能不清楚,具体可以看函数调用时ebp的变化后回来看,
帧指针当前是函数的栈帧的栈底,里面保存的是上一个函数栈帧的 栈底,每次调用子函数之后都会入栈,以便子函数执行完后,跳回原来的地址,在当前函数返回时恢复上个函数的现场
函数调用
栈操作频繁的就是函数调用,而函数调用在栈的操作有不同的约定,如c语言默认调用约定cdecl为
函数参数从右向左进栈
然后返回地址进栈
进入子函数后
栈帧指针进栈
临时变量进栈
调用者清栈
详细描述可以参考
堆栈、栈帧、函数调用过程_堆栈和栈帧_Lemonbr的博客-CSDN博客
另外的调用约定
(2)_stdcall:按从右至左的顺序压参数入栈,由被调用者把参数弹出栈。调用约定在输出
函数名前加上一个下划线前缀,后面加上一个“@”符号和其参数的字节数。
(3)_fastcall:主要特点就是快,因为它是通过寄存器来传送参数的,和__stdcall很
象,唯一差别就是头两个参数通过寄存器传送。注意通过寄存器传送的两个参数是从左向右的,
即第一个参数进ECX,第2个进EDX,其他参数是从右向左的入stack。返回仍然通过EAX。
实例
现在再来看栈帧的一个图
int main(int argc, char *args[])
{
sum(3, 4, 5, 6);
return 0;
}
参考