我们在学校或者各种编程类书本上,通常都会看到一句话:“函数是程序的基本组成单位”。可以说,理解函数对编程是非常重要的,与函数调用紧密结合的机制就是函数调用栈了。而栈有一个特别的属性,那就是栈的增长方向问题了。
1
栈的增长方向
首先,我们要明确的是栈同样也是分布在我们的内存之中,而内存是通过地址来进行编排访问的,如下是堆栈的示意图:
对于堆栈而言原本并没有方向一说,只有入栈和出栈一说,程序中执行push指令则栈顶向上移动,执行pop指令则栈顶向下移动,其仅仅只是一种先进后出的数据结构,增长方向都是从栈底向栈顶方向移动,即分配数据的过程。
而我们平时所说的栈的增长方向又是怎么回事呢?
为了在内存中分配一段内存给堆栈,我们必须要区分堆栈相对于内存的地址而言的方向性,通常栈顶增长的方向是从内存的低地址向高地址变化,我们则称为向上增长;反之则向下增长。
2
有什么用?
当了解处理器中栈指针的增长方向以后,我们在debug程序的时候才能真正的把控程序的运行过程。
在移植RTOS的过程中我们都需要对每个任务的堆栈分配一个合适的连续内存区域来使用,此时初始状态堆栈指针指向什么位置就跟堆栈的增长方向密切相关,有过RTOS移植经验的朋友应该都有在RTOS配置项中关注过这块的选择。
RTOS在任务初始化的时候,其堆栈指针应该指向其栈底位置,那么对于堆栈向上增长,任务初始化的时候我们需要把堆栈指针设置在所分配内存的低地址内存处,反之则设置到高地址处。
设置好以后,其在堆栈分配的过程中才会朝着所分配的内存区域中,否则就会堆栈反向自爆,导致程序异常;如果你的堆栈分配不合理,同样了解堆栈变化方向后也变得有迹可循。
3
用C语言如何判断?
要了解一个CPU的堆栈的变换方向,一方面就是查询相应的芯片参考手册,另外一方面就是实际测试了。
毕竟堆栈也就是内存,自然就可以通过堆栈的分配过程取出所分配的内存地址来比较判断,而C语言可以方便的访问内存,也就比较容易判断当前处理器中堆栈指针的增长方向了。
那还不简单,直接在函数内部先后定义两个局部变量,直接比较两个变量的地址大小不就搞定了吗?其实这种方式是依赖于编译器实现的,毕竟哪个变量先进行内存申请,并没有太大的影响。
那么,是否有一种方法不依赖于编译器实现呢?
必须有的,那就是函数调用栈了,因为先调用的函数必然首先入栈。
基于这样的思想,这里bug菌写一个判断堆栈增长方向的demo供大家参考:
1#include
2#include
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}
感兴趣的小伙伴可以拿去试一试,看看你的芯片堆栈咋变化的~
END
来源:最后一个bug
版权归原作者所有,如有侵权,请联系删除。
▍推荐阅读
我用这个技术,干掉几千行if else!
麒麟9000s,并非来自SMIC,而是...
程序员最容易读错的单词,听到status我炸了