百科狗-知识改变命运!
--

"栈"和"栈帧"这两个概念到底如何区分

是丫丫呀1年前 (2023-12-22)阅读数 7#综合百科
文章标签指针函数

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

免责声明:我们致力于保护作者版权,注重分享,当前被刊用文章因无法核实真实出处,未能及时与作者取得联系,或有版权异议的,请联系管理员,我们会立即处理! 部分文章是来自自研大数据AI进行生成,内容摘自(百度百科,百度知道,头条百科,中国民法典,刑法,牛津词典,新华词典,汉语词典,国家院校,科普平台)等数据,内容仅供学习参考,不准确地方联系删除处理!邮箱:344225443@qq.com)

图片声明:本站部分配图来自网络。本站只作为美观性配图使用,无任何非法侵犯第三方意图,一切解释权归图片著作权方,本站不承担任何责任。如有恶意碰瓷者,必当奉陪到底严惩不贷!

内容声明:本文中引用的各种信息及资料(包括但不限于文字、数据、图表及超链接等)均来源于该信息及资料的相关主体(包括但不限于公司、媒体、协会等机构)的官方网站或公开发表的信息。部分内容参考包括:(百度百科,百度知道,头条百科,中国民法典,刑法,牛津词典,新华词典,汉语词典,国家院校,科普平台)等数据,内容仅供参考使用,不准确地方联系删除处理!本站为非盈利性质站点,本着为中国教育事业出一份力,发布内容不收取任何费用也不接任何广告!)