@allen_zhan:在一般的各种语言编程 help document 中, 都有类似的说法, 其实应该是不建议使用全局变量吧? 而不是这里所说的"没有初始化全局变量". 我猜原因是否是, 2000年代初, SRAM 还是贵的, 资源还是尽量少的, 给 programer 的 RAM 是珍惜的. 所以被我们一旦预约了全局变量, 应该就是等同于占据了 RAM 中的空间. 这是否也意味着"设计的资源效率低下"? 推广到今天的 uController, 当我们使用时, 尽管我们享受着大容量(针对过去)的 RAM, 但是我们也不应该一时兴起, 无休止将大size 的数组定义成全局~~~ 反正这方便我们的代码 coding 不是吗? 但是大 size 的数组, 当然也必将导致可用的 RAM 的迅速降低, 很多时候, 我们茫茫然抬头发现 compiler 告诉我们 RAM 资源耗尽! 但是, 不管是全局的, 还局部的, 无论在哪里进行初始化, 都必然占据 ROMDATA 的. 当然了, 如果预约下全局的 RAMDATA, 而实际上不作初始化, 也事实上代码中发生疏漏, 没有应用, 那更算是粗心导致的大错, 无意义损耗 RAMDATA, 也许这就是所谓的避免定义全局数据的呼吁... 但是哪个心细的 programer 又会犯这种错误呢. 另外, 在某些编程指导书籍里面, 极度反对全局变量, 理由是对工程项目不利, 对团队合作编程不利, 这又是另一个 story 了. 完全不定义全局变量? 似乎在工程学上又走向另一个极端?
事实上, 我有一种隐约的感觉, 无法用明确的表述描绘得清晰. 那就是全局变量和局部变量, 从某种程度上讲, 本质是一致的, 其根本原因就是因为它们都来自 ramdata, 并在某个 routine 中变量有其"生命周期". 局部变量在其"生命周期"内, compiler 给予的 space 的具体内容, 可能被其他的调用可见. 但是一旦超过了这个"生命周期", 那么, 这段空间就被 compiler 释放. 由于我们目前的代码语言, 实际上是函数为核心(另一个流派是 OO 为核心), 这导致我们离开了局部变量的定义的函数, compiler 就释放了局部变量的 RAMDATA 给其他 routine 使用. 也就是说, 架设函数作用时间足够长, 以至于在整个代码生命期(reboot开始算起), 没机会被 compiler 释放, 那么它就是全局的. 之所以在当前的编程语言中, 全局变量与局部变量的明确不同, 则其实被当前的编程语言, 函数决定. 因为我们总是不得不在函数内, 或者函数外来定义变量, 没有其他的途径. 导致函数外的变量被生硬定义为全局, 而我们无法给予"变量生命期"概念. 这时, 我们不得不进行深思, 开始考虑, 如何设计一个系统, 在我们需要或者不需要时预约和释放 RAMDATA 呢? 而不是被迫于函数写法, 被迫决定为局部还是全局呢? ... 这时, 我们便要学习操作系统了吧.
/*----------------------------------------------------------------------------------------------------------------*/
@Catch:
因为在连接时,全局变量和局部静态变量都是在data区,所以在这里给放一起讨论了。在原文中自己的表述犯了一个错误,就是初始化的全局变量或者局部静态变量,是占用生成的可执行文件大小。而未初始化的全局变量和局部静态变量,不占实际文件大小,但是在执行时是分配RAM空间的。这一点,是原来写博文的时候,弄混淆的一点。所以对于RAM而言,局部静态变量和全局变量消耗是一样的。所以,就像你在回复中说的,预约了全局变量可能会导致“设计效率低下”。但是对于ROMDATA而言,初始化和未初始化是不一样的,差异很大。
尽管现在MCU的RAM size大了很多,Flash也多了很多,但由于现在产品功能越来越强,也就要求更多的代码,需要跟多的ROM和RAM。内存受限,可能是个永远存在的问题。但是,也会导致初入的programer,对于内存的问题不会那么敏感。当出现问题的时候,感受到内存限制的时候,可能已经code了很多。
关于禁用全局变量一说,一开始确实感觉很矛盾。有些数据需要以后的函数中用或者在其他的.c中用,如果不用全局怎样进行数据传递。后来看了一些代码,慢慢感觉到用全局变量,与其说是数据的传递,更准确的不如说是怎么将数据保存下来。至于如何将数据传递过去是另一件事情。所以,整个代码中,全局变量存在的意义在于保存数据。如果是这样,我们完全可以使用局部静态变量来保存。然后,通过函数调用将保存的数据,传递给需要的地方(因为函数调用,难免影响效率。对于效率比较高的地方可以用宏定义来实现)。在这个过程中,也感觉到,这样做更好的是,可以减少对于使用变量的关心,减少需要理解的代码(好的函数名是前提),提高程序的可读性,方便以后的修改。
所以,这里我很赞同“全局变量与局部变量的本质差异,也就是生命周期的差异”。既然,静态局部变量和全局变量有相同的生命周期(对于外部来说,可见程度是不一样的),那么我们应该可以最大程度的使用静态局部变量,取代全局变量,通过函数或者宏定义来获取。当然,完全的取代,应该不容易做到。
操作系统的使用在以前的一篇博文里列了自己的看法,操作系统提供一个虚拟的并行,可以让我们将更多的精力focus on我们需要实现的功能上。当然,有些时候为了达到我们需要的效果,对于操作系统的调度算法要有一定了解(虚拟并行毕竟不是真正的并行)。
现在操作系统越来越稳定,在操作系统上的工作量也会越来越少,而应用程序的复杂度却越来越高,如何应用操作系统,实现我们需要的功能。
应用程序构建的三种方法(摘自《UNIX网络编程——进程间通信》):
(1)用一个庞大的程序完成全部工作。程序的各个部分可以实现为函数,函数之间通过参数,返回值和全局变量来交换信息。
(2)使用多个程序,程序之间使用某种形式的IPC进行通信。(因为彼此间运行地址空间独立,可以理解为多个MCU协同工作)
(3)使用一个包含多个线程的程序,线程之间使用某种IPC。(因为彼此间运行地址空间共享,可以理解为一个MCU执行多个任务)
关于静态局部变量初始化与未初始化占用,执行文件大小的验证代码:
初始化静态变量a:
未初始化静态变量a:
关于MCU使用操作系统的一点看法:
1989tie_959541171 2013-12-27 08:19
allen_zhan_752827529 2013-12-26 11:09
唔, 吃了一惊, 突然在 EETC 的email刊物上的一个标题上, 看到我的名字``` 哈哈~~~ catch, 是的, 木有错. 如果我们仅仅预约全局变量, 既不初始化, 又不使用, 那么 compiler will do nothing, 没有任何代码空间被占据, 但是 compiler 已经自作聪明(或者说笨的可以) 约定了被定义的全局变量, 占据了相应的 RAM 空间. 这导致了我们内存的减小. 也就是所谓代码效率降低. 我的观点是清楚的, 就是我以为全局变量需要被谨慎定义, 但是不可消除(或者说消除的必要性不强), 因为它伴生我们要实现的 process 的整个运行(生存)周期, 我们一定有这样的需求存在. 我努力理解了本文, 你从数据传递与保存的观点理解变量, 当然, 这自然是没错的. 我仿佛记得N年前, 我似乎上过一门课程是"信号系统", 还是"信号处理"之类的...请我的老师原谅我吧...99% 的内容都还给他了``` 从数据(信号)的角度来看, 我们执行一个又一个的黑匣子功能, 是使数据(信号)在整个黑匣子的系统 IO 或者内部进行保持传递与更换. 这都是非常正确的理解, 也唤醒了我的某些深入认知. 不过, 你似乎提倡静态局部变量替代全局变量的做法... 还是回到最开始你的讨论, 应该一定避免使用全局变量?``` 因为我很少操作静态具备变量, 所以没什么发言权, 但是简单考虑下, 似乎两者都会被 compiler 用几近相同的方式(我指分配代码空间与预约RAM处理方式)进行处理,,,,我试图猜测, 你是希望用一种打包 wrapper 的方式, 使得静态局部变量在函数访问的方式下, 显得数据传递更规范, 更不易被误用与增加可读性...略略考虑下, 我不持反对意见, 但这将导致代码的工作量增加... 当然, 或者反过来说, 在大型团队项目中, 集体代码工作量减少吧?