什么是堆栈,什么是堆栈?堆栈在嵌入式领域起什么作用,占什么地位?
栈这种结构在嵌入式中其实很常用,比如函数调用和返回就是典型的栈应用,虽然很多时候栈都是 CPU 系统是自动管理的。我们只需要在链接文件中分配堆栈的大小和存储位置,但稍微了解堆栈的原理将更有利于我们了解嵌入式代码执行机制,并帮助我们进一步调试。
堆 HEAP 与栈 STACK 是两种不同的概念,本质上是一种数据结构。
栈是一种只能在一端(栈顶 top)对数据项进行插入和删除,其符合后进先出(Last-In / First-Out)原则。栈(os)一般由编译器自动分配释放,使用一级缓存。
堆也是一种类似链表的数据结构,可以在任何位置操作数据项。(os)二级缓存通常由程序员手动释放。
在嵌入式世界中,堆栈通常指的是堆栈。
在 MCU 中,这种结构一般是 cpu 和 os使用 。
在 cpu 裸机有两种用途:一是主动调用函数时,STACK 用于暂存下一个指令地址、函数参数和函数中定义的局部变量;2、硬中断时,暂存当前执行的现场数据(下一个指令地址和各种缓存数据),中断后恢复。
在 os 使用时,硬栈的使用与 相同cpu但 os 通常为每个任务分配额外的软栈。在任务调度过程中,软中断可以中断当前的任务,而堆栈可以保存各自的任务以恢复。
硬件堆栈通过寄存器 SP 调用 作为索引指针的地址BL 等函数调用指令后,硬件自动填充堆栈。
软件堆栈:编译器为处理某些参数传输而制作的堆栈将由编译器自动生成和处理,并可通过相应的编译选项进行编辑。
简单来说,硬件堆栈主要用作地址堆栈,软件堆栈主要分配成数据堆栈。或者看看它的屋顶指针是否和 一样CPU 有特殊的关联,相关者(如 SP)硬,无关联者软。
基本抽象数据类型(ADT)是编写 C 必要的程序过程,如 ADT 有链表、堆栈、队列、树木等。本节主要讲解堆栈的几种实现方法及其优缺点。
堆栈(stack)后进先出的显著特点是(Last-In First-Out,LIFO),有三种方法可供选择:静态数组、动态分配数组、动态分配链结构。
静态数组:其特点是结构长度固定,编译时必须确定长度。其优点是结构简单,实现方便,不易出错。缺点是不够灵活,固定长度不易控制,适用于知道长度明确的场合。
动态数组:其特点是长度可以在运行过程中确定,原始数组的长度可以更改。优点是灵活,缺点是会增加程序的复杂性。
链结构:其特点是无长度在线,需要时可以最大限度地实现内存空间的灵活性。缺点是链结构的链接字段需要消耗一定的内存,在链结构中访问特定元素的效率不如数组。
首先确定堆栈接口的第一个文件,包含每个方案下的函数原型,以实现程序的模块化和易于修改。然后介绍每个方案的具体实施方法。
堆栈接口 stack.h 文件代码:
在静态数组堆栈中,STACK_SIZE 表示堆栈所能储存的元素的最大值top_element 作为数组下标来表示堆栈中的元素,当 top_element == -1 表示堆栈为空;当 top_element == STACK_SIZE - 1 说明堆栈满了。push 的时候 top_element 加 1,top_element == 0 表示第一堆元素;pop 的时候 top_element 减 1。
a_stack.c 源代码如下:
头文件仍然使用 stack.h,变化不多,增加了 stack_size 变量取代 STACK_SIZE 为了保存堆栈的长度,数组被指针取代,在整体变量下缺少 0。
create_stack 函数首先检查堆栈是否已创建,然后分配所需数量的内存,检查分配是否成功。destroy_stack 函数首先检查堆栈是否存在,释放内存后将长度和指针变量重新设置为零。is_empty 和 is_full 函数中添加了一个断言,以防止在堆栈创建之前调用任何堆栈函数。
d_stack.c 源代码如下:
由于只能访问堆栈的顶部元素,单链表的应用可以很好地实现链堆栈,而且没有长度限制。通过在链表头部添加一个元素,将一个元素压入堆栈。弹出元素是通过删除链表头部的第一个元素来实现的。因为没有长度限制,所以不需要 create_stack 函数,需要 destroy_stack 释放内存以避免内存泄漏。
l_stack.c 源代码如下:



