单片机里的堆栈
单片机里的堆栈
做单片机的应该都听说过堆栈,跟指针一样,一看到这两个就会莫名的蛋疼。但是用汇编的同志肯定不会陌生,因为要经常出栈入栈,但是用C语言的同志有一些可能就比较陌生了,因为出入栈全部交给了编译器。最近我就在这里吃了亏,虽然听说过堆栈,也知道在哪里修改,但因为以前写的代码量很小,变量完全不会让堆栈溢出,所以从来没意识到它的重要性,直到最近写了一个数据量很大的程序,才意识到必须要重视堆栈。
首先说一下它出现的一些现象,应该说无法推断出它将会出现什么现象,因为堆栈溢出后,程序和参数就会全部乱套了,可能不是很要紧也可能让系统崩溃。我第一次发现奇怪的现象是在调试GSM模块通讯的时候,在处理数据的时候大量使用了sprintf标准函数,通讯经常出现异常,断开或者数据错乱,但是我又不熟悉标准函数,所有一直怀疑是使用不合理的问题,就这样整个工程磕磕碰碰写下去,总是感觉工程不稳定,但是怎么看逻辑都没有错,让自己一度抑郁。直到后来抓到一个现象,有两个连续的for循环,第一个正常运行,第二个运行到一半就跳到第一个for循环重新开始,程序终于在这里崩溃了。这非常明显是指针错乱了,让我意识到了堆栈的问题,我把堆栈改大了,但是算错数,结果还是无效,连我自己都崩溃了。好在意志坚定地锁定为堆栈问题,最终解决了。
接下来就说说我修改了哪里吧。其实就仅仅是在stm32中修改了两个数值,一个是栈(stack_size)的大小,一个是堆(heap_size)的大小,如图1-1。
图1-1
堆和栈其实是两个不同的概念,可以这样通俗的去理解,堆是程序猿自己操作的,而栈是编译器帮你操作的。一般在操作系统中,内存是由程序猿自个进行动态分配和回收的,这部分内存就是堆。而我们写的那些个临时变量,通常都是编译器编译的时候自动分配的,这块内存就是栈。
然后就是设置多大的容量比较好呢。我们编译器编译完之后后提示一段信息,如图1-2,这段信息提示了程序内存的使用情况,再具体的分配可以用记事本打开编译后的.map文件,至于具体的表示内容可以参考百度文库https://wenku.baidu.com/view/f13602e403d8ce2f016623a0.html 具体需要设置多大的栈合适呢?这个说实在我也不知道,目前还没遇到RAM内存紧张的情况,所以我一般是把堆栈全部设置为0,编译之后查看RW+ZI的值大小,再酌情取大一点。
图1-2
最后呢,谈谈在程序中应该注意怎么避免栈溢出。第一,尽量不在函数内定义大量的数组。第二,函数的调用不要太深。因为每次调用函数,都需要入栈,把上一个函数所有数据放到栈内,等调用完函数后再出栈把上一个函数的数据恢复,这也就是为什么我会遇到for循环出来指针就乱了。