"栈"和"栈帧"这两个概念到底如何区分
1、栈:FILO先进后出的数据结构
栈底是第一个进栈的数据的位置(压箱?底)?
栈顶是最后一个进栈的数据位置
2、根据SP指针指向的位置,栈可分为?满栈和空栈?
满栈:当sp指针总是指向最后压入堆栈?的数据(ARM采用满栈)
空栈:当堆栈指针SP总是指向下一个将?要放入数据的空位置。
3、根据SP指针移动的方向,可分为升?栈和降栈?
升栈:随数据的入栈,SP由低地址-->?高地址?
降栈:随数据的入栈,SP由高地址-->?低地址(ARM采用降栈)
4、栈帧:存储在用户栈上的(当然内核栈同样适用)每一次函数调用涉及的相关信息的记录单元 ;?栈帧(stack frame)就是一个函数所使用的那部分栈,所有函数的栈帧串起来就组成了一个完整的栈。
栈帧的两个边界分别有FP(R11)和SP(R13)L来限定。?
栈帧
栈的作用:
1)保存局部变量
分析代码:
[html]?view plain?copy
#include?
int?main()
{
int?a;
a++;
return?a;
}
反汇编之后的代码;
[html]?view plain?copy
stack:?file?format?elf32-littlearm
Disassembly?of?section?.text:
00000000?:
#include?
int?main()
{
0:?e52db004?push?{fp}?;?(str?fp,?[sp,?#-4]!)?@将栈帧底部指针FP压入栈中;创建属于main函数的栈帧。
4:?e28db000?addfp,?sp,?#0;?0x0?@fp指针为函数栈帧的底部,
8:?e24dd00c?subsp,?sp,?#12?;?0xc?@sp指针为栈帧的顶部,同时为栈的栈顶。
int?a;
a++;
c:?e51b3008?ldrr3,?[fp,?#-8]?@由此三句可知变量a在栈帧中执行了加法操作,及栈帧具有保存局部变量的作用
10:?e2833001?addr3,?r3,?#1;?0x1
14:?e50b3008?strr3,?[fp,?#-8]
return?a;
18:?e51b3008?ldrr3,?[fp,?#-8]
}
2)保存函数的参数?
分析代码:
[html]?view plain?copy
#include?
void?func1(int?a,int?b,int?c,int?d,int?e,int?f)
{
int?k;
k=e+f;
}
int?main()
{
func1(1,2,3,4,5,6);
return?0;
}
反汇编之后的代码;
void?func1(int?a,int?b,int?c,int?d,int?e,int?f)?@多于4个参数
{
0:?e52db004?push?{fp}?;?(str?fp,?[sp,?#-4]!)@保存main函数的栈帧底部指针FP
4:?e28db000?addfp,?sp,?#0;?0x0
8:?e24dd01c?subsp,?sp,?#28?;?0x1c?@由栈帧顶部指针SP创建一片栈帧保存子函数的前四个参数
c:?e50b0010?strr0,?[fp,?#-16]@?a
10:?e50b1014?strr1,?[fp,?#-20]@?b
14:?e50b2018?strr2,?[fp,?#-24]@?c
18:?e50b301c?strr3,?[fp,?#-28]@?d
int?k;
k=e+f;
1c:?e59b3004?ldrr3,?[fp,?#4]@在子函数的栈帧中实现第五个参数与第六个参数的运算
20:?e59b2008?ldrr2,?[fp,?#8]?@由ldrr2,?[fp,?#8]知参数保存在main函数的栈帧中,并运算
24:?e0833002?addr3,?r3,?r2?@以子函数的栈帧底部指针(fp)做参考坐标实现对参数的查找
28:?e50b3008?strr3,?[fp,?#-8]
}
2c:?e28bd000?addsp,?fp,?#0;?0x0
30:?e8bd0800?pop{fp}
34:?e12fff1e?bx?lr
00000038?:
int?main()
{
38:?e92d4800?push?{fp,?lr}@由于调用子函数,先保存main函数的栈帧底部指针FP和返回地址LR(当前PC指针的下一地址)
3c:?e28db004?addfp,?sp,?#4;?0x4?@可知先压入FP,后压入lr.把此时子函数(被调用者)的栈帧底部指针FP指向保存在子函数栈帧的main函数(调用者)的栈帧底部指针FP
40:?e24dd008?subsp,?sp,?#8;?0x8?@创建栈
func1(1,2,3,4,5,6);
44:?e3a03005?movr3,?#5;?0x5
48:?e58d3000?strr3,?[sp]
4c:?e3a03006?movr3,?#6;?0x6
50:?e58d3004?strr3,?[sp,?#4]
54:?e3a00001?movr0,?#1;?0x1?@用通用寄存器保存前四个参数的值
58:?e3a01002?movr1,?#2;?0x2
5c:?e3a02003?movr2,?#3;?0x3
60:?e3a03004?movr3,?#4;?0x4
64:?ebfffffe?bl?0?
return?0;
68:?e3a03000?movr3,?#0;?0x0
}
6c:?e1a00003?movr0,?r3
70:?e24bd004?subsp,?fp,?#4;?0x4
74:?e8bd4800?pop{fp,?lr}
78:?e12fff1e?bx?lr
注:C中,若函数的参数小于等于4个,则用通用寄存器保存其参数值,多于4个的参数保存在栈中
3)保存寄存器的值
分析代码:
[html]?view plain?copy
include?
void?func2(int?a,int?b)
{
int?k;
k=a+b;
}
void?func1(int?a,int?b)
{
int?c;
func2(3,4);
c=a+b;
}
int?main()
{
func1(1,2);
return?0;
}
反汇编之后的代码;
[html]?view plain?copy
void?func2(int?a,int?b)
{
0:?e52db004?push?{fp}?;?(str?fp,?[sp,?#-4]!)
4:?e28db000?addfp,?sp,?#0;?0x0
8:?e24dd014?subsp,?sp,?#20?;?0x14
c:?e50b0010?strr0,?[fp,?#-16]?@保存寄存器的值
10:?e50b1014?strr1,?[fp,?#-20]
int?k;
k=a+b;
14:?e51b3010?ldrr3,?[fp,?#-16]
18:?e51b2014?ldrr2,?[fp,?#-20]
1c:?e0833002?addr3,?r3,?r2
20:?e50b3008?strr3,?[fp,?#-8]
}
24:?e28bd000?addsp,?fp,?#0;?0x0
28:?e8bd0800?pop{fp}
2c:?e12fff1e?bx?lr
00000030?:
void?func1(int?a,int?b)
{
30:?e92d4800?push?{fp,?lr}
34:?e28db004?addfp,?sp,?#4;?0x4
38:?e24dd010?subsp,?sp,?#16?;?0x10
3c:?e50b0010?strr0,?[fp,?#-16]?@代码44行调用func2函数后,又使用r0\r1保存参数,所以此时将r0\r1寄存器的
40:?e50b1014?strr1,?[fp,?#-20]@值放入栈中
int?c;
func2(3,4);
44:?e3a00003?movr0,?#3;?0x3
48:?e3a01004?movr1,?#4;?0x4
4c:?ebfffffe?bl?0?
c=a+b;
50:?e51b3010?ldrr3,?[fp,?#-16]
54:?e51b2014?ldrr2,?[fp,?#-20]
58:?e0833002?addr3,?r3,?r2
5c:?e50b3008?strr3,?[fp,?#-8]
}
60:?e24bd004?subsp,?fp,?#4;?0x4
64:?e8bd4800?pop{fp,?lr}
68:?e12fff1e?bx?lr
0000006c?:
int?main()
{
6c:?e92d4800?push?{fp,?lr}
70:?e28db004?addfp,?sp,?#4;?0x4
func1(1,2);
74:?e3a00001?movr0,?#1;?0x1
78:?e3a01002?movr1,?#2;?0x2
7c:?ebfffffe?bl?30?
return?0;
80:?e3a03000?movr3,?#0;?0x0
}
84:?e1a00003?movr0,?r3
88:?e24bd004?subsp,?fp,?#4;?0x4
8c:?e8bd4800?pop{fp,?lr}
90:?e12fff1e?bx?lr
初始化栈:即对SP指针赋予一个内存地址(统一标准:2440、6410、210)
在内存的64MB位置即ldr sp, =0x34000000(2440)
ldr sp, =0x54000000(6410)
ldr sp, =0x24000000(210)
由上可知ARM采用满栈(指向刚入栈的数据)、降栈(由高地址向低地址入栈)
问题:因为ARM不同工作模式有不同的栈,定义栈的技巧是什么,避免定义相同的地址使用不同栈?
转自:http://blog.csdn.net/u011467781/article/details/39559737
栈的最主要特点是后进先出。
栈是一种运算受限的线性表,限定仅在表尾进行插入和删除操作的线性表。栈的所有插入和删除操作均在栈顶进行,而栈底不允许插入和删除。
向一个栈插入新元素又称作进栈、入栈或压栈,它是把新元素放到栈顶元素的上面,使之成为新的栈顶元素;从一个栈删除元素又称作出栈或退栈,它是把栈顶元素删除掉,使其相邻的元素成为新的栈顶元素。
扩展资料:
栈作为一种数据结构,是一种只能在一端进行插入和删除操作的特殊线性表。它按照先进后出的原则存储数据,先进入的数据被压入栈底,最后的数据在栈顶。
1、进栈(PUSH)算法
①若TOP≥n时,则给出溢出信息,作出错处理(进栈前首先检查栈是否已满,满则溢出;不满则作②);
②置TOP=TOP+1(栈指针加1,指向进栈地址);
③S(TOP)=X,结束(X为新进栈的元素);
2、退栈(POP)算法
①若TOP≤0,则给出下溢信息,作出错处理(退栈前先检查是否已为空栈, 空则下溢;不空则作②);
②X=S(TOP),(退栈后的元素赋给X):
③TOP=TOP-1,结束(栈指针减1,指向栈顶)。
鹏仔微信 15129739599 鹏仔QQ344225443 鹏仔前端 pjxi.com 共享博客 sharedbk.com
图片声明:本站部分配图来自网络。本站只作为美观性配图使用,无任何非法侵犯第三方意图,一切解释权归图片著作权方,本站不承担任何责任。如有恶意碰瓷者,必当奉陪到底严惩不贷!