我是老温,一名热爱学习的嵌入式工程师
; e& M. J6 N& x# ?, g( O+ c关注我,一起变得更加优秀!嵌入式开发,特别是单片机os-less的程序,最易范的错误是全局变量满天飞。! ] {5 W1 b8 t5 Q$ \* r
这个现象在早期汇编转型过来的程序员以及初学者中常见,这帮家伙几乎把全局变量当作函数形参来用。
- G+ m' B4 U1 a" d- t2 a/ B在.h文档里面定义许多杂乱的结构体,extern一堆令人头皮发麻的全局变量,然后再这个模块里边赋值123,那个模块里边判断123分支决定做什么。
# `0 d* [) K& ~$ G% \每当看到这种程序,我总要戚眉变脸而后拍桌怒喝。没错,就是怒喝。
' \5 P: y! f: o4 N$ C不否认全局变量的重要性,但我认为要十分谨慎地使用它,滥用全局变量会带来其它更为严重的结构性系统问题。& s% V* Z1 ?: p- a4 K( B& G! I0 q
l1qpj01wtoc64037227745.png
6 B: ~8 Y6 f' I. ?- j为什么全局变量要越少越好?
* v6 V: g) W- L$ Q5 }它会造成不必要的常量频繁使用,特别当这个常量没有用宏定义“正名”时,代码阅读起来将万分吃力。
4 Y! A2 N, {5 g, g: D0 B3 g它会导致软件分层的不合理,全局变量相当于一条快捷通道,它容易使程序员模糊了“设备层”和“应用层”之间的边界。写出来的底层程序容易自作多情地关注起上层的应用。4 e* r" T+ D$ w6 y7 p, |; r6 _
这在软件系统的构建初期的确效率很高,功能调试进度一日千里,但到了后期往往bug一堆,处处“补丁”,雷区遍布。说是度日如年举步维艰也不为过。; t' i; M0 `9 l1 a& W
由于软件的分层不合理,到了后期维护,哪怕仅是增加修改删除小功能,往往要从上到下掘地三尺地修改,涉及大多数模块,
$ T6 e5 d) q1 W L而原有的代码注释却忘了更新修改,这个时候,交给后来维护者的系统会越来越像一个“泥潭”,注释的唯一作用只是使泥潭上方再加一些迷烟瘴气。
& t/ w: n# ]; F全局变量大量使用,少不了有些变量流连忘返于中断与主回圈程序之间。
H, E, f V2 }% ]这个时候如果处理不当,系统的bug就是随机出现的,无规律的,这时候初步显示出病入膏肓的特征来了,没有大牛来力挽狂澜,注定慢性死亡。
( @4 C6 w( f/ g2 j7 N. B: m. y无需多言,您已经成功得到一个畸形的系统,它处于一个神秘的稳定状态!
) O- A4 X2 G$ z6 o! ]你看着这台机器,机器也看着你,相对无言,心中发毛。你不确定它什么时候会崩溃,也不晓得下一次投诉什么时候道理。
# `- x9 m7 [6 t# m; f- g全局变量大量使用有什么后果?1 A* r0 Z; `0 y1 a2 m1 M7 [" C2 S
“老人”气昂昂,因为系统离不开他,所有“雷区”只有他了然于心。当出现紧急的bug时,只有他能够搞定。你不但不能辞退他,还要给他加薪。9 M# j# X7 {; b& p b! S
新人见光死,但凡招聘来维护这个系统的,除了改出更多的bug外,基本上一个月内就走人,到了外面还宣扬这个公司的软件质量有够差够烂。
7 ]6 Z! |1 E$ p/ l随着产品的后续升级,几个月没有接触这个系统的原创者会发现,很多雷区他本人也忘记了,于是每次的产品升级维护周期越来越长,: d/ Z0 G, ]1 _8 \% r6 J) x' _
因为修改一个功能会冒出很多bug,而按下一个bug,会弹出其他更多的bug。在这期间,又会产生更多的全局变量。( {7 ~! K- m( L9 N
终于有一天他告诉老板,不行啦不行啦,资源不够了,ram或者flash空间太小了,升级升级。
% f |/ K+ ]/ S8 I客户投诉不断,售后也快崩溃了,业务员也不敢推荐此产品了,市场份额越来越小,公司形象越来越糟糕。
, Q$ t: \! z$ x1 e) ]
ib1ddhqhnrt64037227845.jpg
0 l. j3 P. F! `, w& K; [
" @# c% _+ V4 c! x2 Y要问对策,只有两个原则
1 ]% I7 c/ y$ f能不用全局变量尽量不用,我想除了系统状态和控制参数、通信处理和一些需要效率的模块,其他的基本可以靠合理的软件分层和编程技巧来解决。$ q. r- r+ ~% J0 {
如果不可避免需要用到,那能藏多深就藏多深。5 P9 }! _' E' I, j
如果只有某.c文件用,就static到该文件中,顺便把结构体定义也收进来;如果只有一个函数用,那就static到函数里面去;
: Z% o& `, Y, V3 v如果非要开放出去让人读取,那就用函数return出去,这样就是只读属性了;5 w5 s6 b9 u8 ^3 ~) q
如果非要遭人蹂躏赋值,好吧,我开放函数接口让你传参赋值;实在非要extern侵犯我,我还可以严格控制包含我.h档的对象,而不是放到公共的includes.h中被人围观,丢人现眼。
* Z/ P' d2 B. E& g. `) `如此,你可明白我对全局变量的感悟有多深刻,悲催的我,已经把当年那些“老人”交给我维护的那些案子加班全部重新翻写了。; R- H) t& o" C: L3 N
最后补充9 o$ T* {: k, C! A. y
全局变量是不可避免要用到的,每一个设备底层几乎都需要它来记录当前状态,控制时序,起承转合。但是尽量不要用来传递参数,这个很忌讳的。4 X( E" d( K& R+ N3 f
尽量把变量的作用范围控制在使用它的模块里面,如果其他模块要访问,就开个读或写函数接口出来,严格控制访问范围。
6 j# Y- {1 J T5 d7 S4 s这一点,C++的private属性就是这么干的,这对将来程序的调试也很有好处。% G6 S% Q) @5 j1 D0 q1 N
C语言之所以有++版本,很大原因就是为了控制它的灵活性,要说面向对象的思想,C语言早已有之,亦可实现。. ], m! C5 x' M5 s3 |" n
当一个模块里面的全局变量超过3个(含)时,就用结构体包起来吧,要归0便一起归0,省得丢三落四的。. {9 w: Z1 `2 G; G" Z+ J9 Q+ \& B
在函数里面开个静态的全局变量,全局数组,是不占用栈空间的,只是有些编译器对于大块的全局数组,会放到和一般变量不同的地址区。
! f; c- l* v1 ^( i若是在keil C51,因为是静态编译,栈爆掉了会报警,所以大可以尽情驰骋,注意交通规则就是了。
' p! W0 X p L7 t单片机的os-less系统中,只有栈没有堆的用法,那些默认对堆分配空间的“startup.s”,可以大胆的把堆空间干掉。
- o% b* J8 h/ n* s% W程序模型?如何分析抽象出来呢,从哪个角度进行模型构建呢?很愿意聆听网友的意见。
7 ]* ?3 B6 m$ n/ v' t" M( g本人一直以来都是从两个角度分析系统,事件--状态机迁移图 和 数据流图,前者分析控制流向,完善UI,后者可知晓系统数据的缘起缘灭。
6 C* J* U9 n' [; g1 H8 P) D! J2 S这些理论,院校的《软件工程》教材都有,大家不妨借鉴下。只不过那些理论,终究是起源于大型系统软件管理的,牛刀杀鸡,还是要裁剪一下的。
0 b' r7 F" e' z5 O- L& v) v来源:网络。
- ]6 H* l' A1 Z1 H8 z-END-
3 f1 D7 s' a9 L: D9 B Y往期推荐:点击图片即可跳转阅读
4 o' o. K/ u5 Z
" {2 Y: d6 v7 {" ~$ c 8 r) ]0 a) ?5 m; i9 m$ t0 t( C, R
- y' j( g" t3 |- Q: G
2 Y& B* n- V' w1 V
bd0p1tudmrp64037227945.jpg
0 ^4 E' o3 u9 k) K5 q+ V$ V l* O7 ? w. F0 Y9 S% n3 R
嵌入式大佬分享,简单易用的开发工具及解决方案! _6 O( c- e; Q" H
5 `8 `6 B L- u8 S6 N7 V ' l0 H D* c- a8 v/ o
: s+ X$ N V6 e' G/ p+ C/ G ' n2 c0 K4 ^5 M% C8 t E
bixmlpz5yfp64037228045.jpg
) e, {; C6 J. M0 X# [' K. N7 } " d9 Q# t" \. o
一些非常全面的嵌入式软件开发工具!
- N3 k; p" r2 x0 e9 Y1 G * V) D% _1 k* n8 ~
( j2 j& _$ Q4 B0 I4 O& @
) H$ k* x2 r9 h6 b2 P- ?
bpb53fw1b3y64037228145.jpg
3 h# P6 a w8 ], y
4 B. c" b$ R/ Q. S# z/ Y" b 探讨一下,嵌入式边缘计算技术,前景何在?
, p- N9 w) R: Z$ |9 e: {4 U
/ B) |" k! Q6 {; g3 k2 `) z $ o' e6 f Z& v7 e
+ r7 H7 Z( a p T$ h; P S
我是老温,一名热爱学习的嵌入式工程师" T+ Z* C* `7 Z+ k1 N/ {
关注我,一起变得更加优秀! |