栈的增长方向怎么理解?用C语言如何判断?

804c0d165579f80312040571220d81c8.gif

正文


大家好,我是bug菌~

在学校或者各种编程类书本上,基本上都会看到一句话:"函数是程序的基本组成单位",可以说理解函数对编程是非常重要的,与函数调用紧密结合的机制就是函数调用栈了,而栈有一个特别的属性就是栈的增长方向问题了,也发现一些多年编程经验的朋友对这一块都有点迷迷糊糊的。

在阅读RTOS源码的时候也会经常看到栈的增长方向配置项目,那么今天bug菌就带大家了解一下栈的增长方向到底是咋回事。

1

栈的增长方向

首先我们要明确的是栈同样也是分布在我们的内存之中,而内存是通过地址来进行编排访问的,如下是堆栈的示意图:

d0159aa7f9cfbd52570bd43f44e63b5e.png

对于堆栈而言原本并没有方向一说,只有入栈和出栈一说,程序中执行push指令则栈顶向上移动,执行pop指令则栈顶向下移动,其仅仅只是一种先进后出的数据结构,增长方向都是从栈底向栈顶方向移动,即分配数据的过程。

而我们平时所说的栈的增长方向又是怎么回事呢?

为了在内存中分配一段内存给堆栈,我们必须要区分堆栈相对于内存的地址而言的方向性,通常栈顶增长的方向是从内存的低地址向高地址变化,我们则称为向上增长;反之则向下增长。

所谓"水往高处流,即向上增长",这样应该就很好记忆了。

2

有什么用?

当了解处理器中栈指针的增长方向以后,我们在debug程序的时候才能真正的把控程序的运行过程。

在移植RTOS的过程中我们都需要对每个任务的堆栈分配一个合适的连续内存区域来使用,此时初始状态堆栈指针指向什么位置就跟堆栈的增长方向密切相关,有过RTOS移植经验的朋友应该都有在RTOS配置项中关注过这块的选择。

RTOS在任务初始化的时候,其堆栈指针应该指向其栈底位置,那么对于堆栈向上增长,任务初始化的时候我们需要把堆栈指针设置在所分配内存的低地址内存处,反之则设置到高地址处。

e18fde4dc1b892094b778156ad63a6a9.png

设置好以后,其在堆栈分配的过程中才会朝着所分配的内存区域中,否则就会堆栈反向自爆,导致程序异常;如果你的堆栈分配不合理,同样了解堆栈变化方向后也变得有迹可循。

同样在裸机程序中也需要了解一下处理器的堆栈变化方向,从而用来排查一些堆栈溢出所导致的程序异常问题。

3

用C语言如何判断?

要了解一个CPU的堆栈的变换方向,一方面就是查询相应的芯片参考手册,另外一方面就是实际测试了。

毕竟堆栈也就是内存,自然就可以通过堆栈的分配过程取出所分配的内存地址来比较判断,而C语言可以方便的访问内存,也就比较容易判断当前处理器中堆栈指针的增长方向了。

那还不简单,直接在函数内部先后定义两个局部变量,直接比较两个变量的地址大小不就搞定了吗?其实这种方式是依赖于编译器实现的,毕竟哪个变量先进行内存申请,并没有太大的影响。

那么是否有一种方法不依赖于编译器实现呢?

必须有的,那就是函数调用栈了,因为先调用的函数必然首先入栈。

基于这样的思想,这里bug菌写一个判断堆栈增长方向的demo供大家参考:

1#include <stdio.h>
 2#include <stdlib.h>
 3
 4#define STACK_UP (0)
 5#define STACK_DN (1)
 6
 7/***************************************
 8@ Function: find_stack_direction
 9@ Author  : bug man 
10@ Note    : (公众号:最后一个bug)
11****************************************/
12int find_stack_direction(int* ptr)
13{
14    int  Val = 0;
15
16    printf("Last stack Addr : %p\n",ptr);
17    printf("Now  stack Addr : %p\n",&Val);
18
19    if(ptr > &Val)
20    {
21        return STACK_DN;
22    }
23
24    return STACK_UP;
25} 
26/***************************************
27@ Function: main
28@ Author  : bug man 
29@ Note    : (公众号:最后一个bug)
30****************************************/
31int main(int argc, char *argv[]) {
32    int  Val = 0;
33
34    printf("stack direction : %d\n",find_stack_direction(&Val));
35    return 0;
36}

可以拿去试一试,看看你的芯片堆栈咋变化的~

最后

      好了,今天就跟大家分享这么多了,如果你觉得有所收获,一定记得点个~

最后一个bug,bug菌唯一创作平台~

5d7e2cde4ec9ed262afec1838e21794e.png

推荐专辑  点击蓝色字体即可跳转

☞  MCU进阶专辑 e21ae2eed774996554fdcf49bdea3411.gif

☞  嵌入式C语言进阶专辑 be3d09a2a5527598504c73bef6c9d88d.gif

☞  “bug说”专辑 a9659e84f0cf6cd67dfbda155913e372.gif

‍‍‍‍‍‍‍‍☞ 专辑|Linux应用程序编程大全

☞ 专辑|学点网络知识

☞ 专辑|手撕C语言

☞ 专辑|手撕C++语言

☞ 专辑|经验分享

☞ 专辑|电能控制技术

☞ 专辑 | 从单片机到Linux‍‍‍‍‍‍‍‍

a83207fa89e442014c426356bbac4200.gif

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值