使用queue实现stack

本文介绍了一种使用两个队列实现自定义栈的方法,包括初始化、压栈、弹栈、获取栈顶元素和判断栈是否为空等操作。通过巧妙地切换输入队列和输出队列,实现了栈的基本功能。

 

class MyStack {
public:
    /** Initialize your data structure here. */
    MyStack() {
        input = new queue<int>();
        output = new queue<int>();
    }
    
    /** Push element x onto stack. */
    void push(int x) {
        input->push(x);
        while(!output->empty()){
            input->push(output->front());
            output->pop();
        }
        queue<int> *p = input;
        input = output;
        output = p;
    }
    
    /** Removes the element on top of the stack and returns that element. */
    int pop() {
        if(output->empty()) return 0;
        int r = output->front();
        output->pop();
        return r;
    }
    
    /** Get the top element. */
    int top() {
        return output->front();
    }
    
    /** Returns whether the stack is empty. */
    bool empty() {
        return output->empty();
    }
private : 
    queue<int>* input,*output;
};

 

<think>我们正在讨论的是嵌入式系统中的底层编程,特别是关于STM32启动文件中的`DCD STACK_TOP`指令。根据用户提供的引用[1]和引用[4],我们可以知道这个指令出现在STM32的启动文件中,用于定义中断向量表。下面我将详细解释`DCD STACK_TOP`的作用和使用场景。 ### 一、`DCD STACK_TOP`的作用 在ARM Cortex-M系列微控制器(如STM32)的启动文件中,`DCD STACK_TOP`用于初始化主堆栈指针(MSP)。具体来说: 1. **位置**:它是中断向量表的第一个条目(位于地址0x00000000)。 2. **功能**:`DCD`是汇编伪指令,表示分配一个32位的字(4字节)。`STACK_TOP`是一个符号,代表栈顶地址(通常是RAM的末尾地址,因为栈在ARM中向下增长)。 3. **硬件行为**:当处理器复位时,硬件会自动从向量表的第一个条目加载值到主堆栈指针(MSP)寄存器[^1][^4]。 > **数学表达**: > 设向量表起始地址为$V_0$,则: > $$ \text{MSP}_{\text{复位后}} = \text{Memory}[V_0] = \text{STACK\_TOP} $$ ### 二、使用场景 1. **系统启动初始化**: - 在复位后,Cortex-M内核首先将`STACK_TOP`的值赋给MSP,确保后续代码(如`__main`初始化)有可用的栈空间。 - 示例代码片段(来自启动文件): ```assembly __Vectors DCD __initial_sp ; Top of Stack DCD Reset_Handler ; Reset Handler DCD NMI_Handler ; NMI Handler ... ; 其他中断向量 ``` 其中`__initial_sp`就是链接脚本中定义的`STACK_TOP`[^1]。 2. **内存布局关联**: - `STACK_TOP`的值由链接脚本(如`.ld`文件)定义,指向RAM区域的末尾(例如`0x20010000`)。 ```ld MEMORY { RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 64K } _estack = ORIGIN(RAM) + LENGTH(RAM); /* 定义栈顶 */ ``` ### 三、技术细节 - **栈增长方向**:ARM Cortex-M的栈是**满递减**(Full Descending),即栈指针指向最后一个压入的元素,且向低地址增长。 - **多栈支持**: 在RTOS或复杂应用中,除主栈(MSP)外,任务可能使用进程栈指针(PSP)。但`DCD STACK_TOP`仅初始化MSP。 - **错误处理**:若未正确定义`STACK_TOP`,会导致栈溢出(覆盖数据)或栈下溢(访问非法内存),引发硬件错误[^4]。 ### 四、实际工程案例 假设STM32F407的RAM地址范围为`0x20000000–0x20020000`(128KB),则: 1. 链接脚本定义栈顶: ```ld _estack = 0x20020000; /* 栈顶 = RAM结束地址 */ ``` 2. 启动文件引用该符号: ```assembly AREA RESET, DATA, READONLY EXPORT __Vectors __Vectors DCD _estack ; MSP初始值 DCD Reset_Handler ; 复位向量 ... ``` ### 五、与其他架构的对比 | 架构 | 栈指针初始化方式 | |--------------|------------------------------------------| | ARM Cortex-M | 由向量表第一个条目(`DCD STACK_TOP`)提供 | | x86 | 由引导加载程序设置ESP | | RISC-V | 由`mtvec`(机器陷阱向量)或启动代码设置 | --- ### 总结 `DCD STACK_TOP`在ARM Cortex-M启动流程中的核心作用是**初始化主堆栈指针(MSP)**,确保系统复位后立即拥有有效的栈空间。其值由链接脚本定义,指向RAM的物理末尾地址。这一机制是嵌入式系统可靠运行的基础[^1][^4]。 --- §§相关问题§§ 1. 在STM32的链接脚本中,如何精确控制栈和堆的内存分配? 2. 若中断向量表中`DCD STACK_TOP`指向了错误地址,系统会出现什么现象?如何调试? 3. 在多任务系统中(如FreeRTOS),任务栈是如何初始化的?与主栈有何关系? 4. ARM Cortex-M的异常处理机制中,栈指针(MSP/PSP)切换是如何触发的? 5. 如何通过调试器(如GDB)实时监控栈指针的变化?</think>### `DCD STACK_TOP` 的作用及使用场景详解 #### 一、核心作用:初始化主堆栈指针(MSP) 在 ARM Cortex-M 架构的启动文件中(如 STM32),`DCD STACK_TOP` 是中断向量表(Vector Table)的第一个条目,其核心作用是**定义主堆栈指针(MSP)的初始值**: ```assembly __Vectors DCD STACK_TOP ; 地址0x00000000:主堆栈指针初始值 DCD Reset_Handler ; 地址0x00000004:复位中断向量 ... ; 其他中断向量 ``` 1. **硬件自动加载机制** 当芯片复位时,ARM 内核会**自动从地址 0x00000000 读取 4 字节数据**,将其加载到 MSP 寄存器中[^1][^4]。 $$ \text{MSP}_{\text{reset}} = \text{Memory}[0x00000000] = \text{STACK\_TOP} $$ 2. **栈方向设计** ARM 栈采用**满递减模型**(Full Descending Stack),即: - 栈指针指向最后压入的有效数据 - 栈向低地址方向增长 ```c PUSH {R0} → MSP = MSP - 4 // 栈向下增长 POP {R0} → MSP = MSP + 4 ``` #### 二、关键使用场景 1. **系统启动初始化** - 芯片上电复位后,**第一个执行的机器指令是加载 MSP** - 确保后续代码(如 `Reset_Handler`)有有效的栈空间 - 示例流程: ```mermaid graph LR A[上电复位] --> B[硬件自动加载 MSP = STACK_TOP] B --> C[跳转到 Reset_Handler] C --> D[C 语言环境初始化] ``` 2. **中断处理基础** - 发生中断时,处理器自动使用 MSP 保存上下文: ```assembly ; 硬件自动完成 MSP = MSP - 8 ; 预留空间 Memory[MSP] = PC ; 保存返回地址 Memory[MSP+4] = PSR; 保存程序状态 ``` - 嵌套向量中断控制器(NVIC)依赖正确初始化的栈[^4] 3. **多任务系统支持** 在 RTOS 中: - MSP 用于内核和异常处理 - 各任务使用独立的进程栈指针(PSP) - 上下文切换时通过 `MSR PSP, R0` 切换栈指针 #### 三、技术实现细节 1. **STACK_TOP 的来源** 通常在链接脚本(.ld 文件)中定义: ```ld _estack = ORIGIN(RAM) + LENGTH(RAM); /* 栈顶 = RAM 末尾地址 */ ``` - 例如 RAM 地址范围 `0x20000000-0x20020000` → `STACK_TOP=0x20020000` 2. **内存布局示例** | 地址 | 内容 | 说明 | |-------------|--------------------|-----------------------| | 0x00000000 | `STACK_TOP` | MSP 初始值 | | 0x00000004 | `Reset_Handler` | 复位中断入口 | | ... | ... | 其他中断向量 | | 0x20000000 | 堆区 (Heap) | 动态内存分配 | | ↓ | 全局变量 | | | ↑ | 栈区 (Stack) | 向低地址增长 | | 0x20020000 | `STACK_TOP` | 栈顶(初始空栈位置) | 3. **错误配置的后果** - 栈溢出:`STACK_TOP` 设置过低 → 栈覆盖全局变量 - 栈下溢:`STACK_TOP` 超出 RAM → 访问非法内存引发 HardFault #### 四、实际工程案例 1. **裸机系统启动流程** ```assembly ; startup_stm32f4xx.s __Vectors DCD _estack ; MSP 初始值 DCD Reset_Handler ; 复位处理入口 DCD NMI_Handler ... Reset_Handler: LDR r0, =_estack ; 显式加载栈指针(可选) MOV sp, r0 BL SystemInit ; 初始化时钟等 BL __main ; 跳转到 C 环境 ``` 2. **FreeRTOS 中的双栈机制** ```c void xPortStartScheduler(void) { // 1. 使用 MSP 运行内核 __asm volatile("msr msp, %0" ::"r"(pxTopOfMainStack)); // 2. 创建任务时初始化任务栈 pxNewTCB->pxStack = pxStackBuffer; pxNewTCB->pxTopOfStack = pxStackBuffer + ulStackDepth - 1; // 3. 任务切换时加载 PSP portRESTORE_CONTEXT(); } ``` #### 五、与其他架构对比 | 架构 | 栈指针初始化方式 | |-------------|--------------------------------------| | ARM Cortex-M| 通过向量表首条目(`DCD STACK_TOP`) | | x86 | 由引导加载程序设置 ESP | | RISC-V | 通过 `mtvec` 寄存器指定初始 PC 和 SP | ### 总结 `DCD STACK_TOP` 在 ARM Cortex-M 系统中承担着**初始化主堆栈指针**的关键职责: 1. **硬件强制动作**:复位时自动加载到 MSP 寄存器 2. **内存布局锚点**:定义栈区的起始位置(最高地址) 3. **系统安全基础**:确保中断处理和函数调用有可靠栈空间 4. **RTOS 支撑基石**:实现内核栈与任务栈的分离管理 正确配置 `STACK_TOP` 是嵌入式系统可靠运行的先决条件,需通过链接脚本精确对齐物理 RAM 边界[^1][^4]。 --- **相关问题** 1. 在 STM32 链接脚本中如何精确控制栈和堆的内存分配? 2. 若中断向量表中 `DCD STACK_TOP` 指向错误地址会导致什么故障?如何调试? 3. FreeRTOS 任务切换时如何实现 MSP 到 PSP 的转换? 4. ARM Cortex-M 的栈溢出检测机制是如何工作的? 5. 在多核系统中(如 Cortex-A 系列)栈初始化有何不同?
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值