嵌入式开发中广泛采用全局变量的考虑
嵌入式开发中广泛采用全局变量的考虑
2024-06-02 10:26:57 星期日
在知乎上看到这个问题,C语言开发单片机为什么大多数都采用全局变量的形式?,才发觉在嵌入式开发中,使用全局变量的广泛程度远远高于纯软开发。在最近半年的TI CC2530 Zigbee开发和在实习单位的开发工作过程中,对全局变量的使用也有一些思考和体悟,顺便记录。
函数与函数之间的“接口”
作为函数之间的“接口”,这点在最近写的课程作业多模块任务划分算法中体现的多。在实现算法的过程中,函数1把一个全局变量经过一系列复杂的算法计算后改变了这个全局变量的值,然后函数2再拿着函数1处理过的这个全局变量再做另外的处理。
优势:
- 即使没有阅读过相关算法,也能通过函数执行过程快速掌握算法,因为几乎只是算法的代码化。
- 便于调试,只有一个变量,在需要位置打断点或者打印即可。
- 快,访问变量总比调用一个获取函数快的多。
劣势:
- 大量的全局变量占用大量资源,尤其在嵌入式这个寸块寸金的领域。
- 全局变量不符合软件工程的思想,随便且大量的修改带来Bugfix上的困难。(你当这里是公共厕所嘛)
中断驱动决定很多变量无法用参数传递
嵌入式软件远远没有特殊到不需要按照正常软件工程方法去管理的地步。要真有人认为“嵌入式软件只要能工作就成,代码丑一点无所谓的”纯粹是软件工程水平不行,不是因为控制水平太高。
全局变量的使用规则,也是单位目前正在使用的部分规则:
- 如果只文件内调用,全局变量只能写在这个.c文件里,不要写进.h文件。
- 如果有文件外调用,全局变量的声明要写在.h文件里,定义写在.c文件里。
2.1 当你想在.h文件中声明一个全局变量,但是不在.h文件中定义,而是在其他的.c文件中定义它,使用extern
关键字,表示变量在其他地方定义,需要编译器去寻找。 - 所有的全局变量无论在.h还是.c里面可以包成同文件名的Struct,这样其他人不仅可以立即识别出一个变量是Project内Global/文件内Static/函数内Local,同时还能追溯到函数是属于哪个文件的。
3.1 这点见仁见智。定义成结构体后,会浪费部分因对齐而导致的内存没有利用的问题,通过Autosar标准下的Memmap,避免这个问题的同时,能够规则合理分配全局变量在段中的位置了。 - 避免函数内的
static
变量。函数内static
变量在实际的项目中几乎就是Bug生成器,没法简单的Reset。而且对单元测试非常不友好。 - 获取其他非
extern
的全局变量,通过接口函数的方式获取。
可预测的内存占用对嵌入式更重要
可以认为变量有三种存储方法:
静态分配:在程序启动时就一次性分配好了,全局变量,静态变量都是如此
栈分配:栈空间随着函数调用分配,调用完指针回去就算是释放了
动态分配:如上所述,很麻烦,很慢
对单片机程序来说,可预测的内存占用比较重要。这决定了要不要换单片机。静态分配可以在程序编译过程就知道所需的内存,且分配过程也是在main()函数之前的startup汇编就给分配好的。所以静态分配也就成了单片机程序的好选择。
栈分配有一定的动态性,使得整体占用的内存空间不那么好预测。且单片机程序一般把栈都设置的较小,不好的程序有栈溢出的风险。栈相对动态分配的优势是,栈空间的申请和释放很简单,所以并不算不可接受。
动态内存分配就真的不太适合单片机了。我甚至见过有些单片机或实时系统的开发环境里,要求程序员主动选择内存分配的实现算法。对内存最有效的分配算法,所占用的时间和额外的内存也是很高的。
超级大的全局变量导致的问题
资源不够,强行修改堆栈大小通过编译,却又发送内存践踏这种更难排查的问题。