原创 回复@allen_zhan关于编写可靠代码的一些记录

2013-12-24 13:41 1239 18 20 分类: 消费电子

@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而言,初始化和未初始化是不一样的,差异很大。

尽管现在MCURAM size大了很多,Flash也多了很多,但由于现在产品功能越来越强,也就要求更多的代码,需要跟多的ROMRAM。内存受限,可能是个永远存在的问题。但是,也会导致初入的programer,对于内存的问题不会那么敏感。当出现问题的时候,感受到内存限制的时候,可能已经code了很多。

关于禁用全局变量一说,一开始确实感觉很矛盾。有些数据需要以后的函数中用或者在其他的.c中用,如果不用全局怎样进行数据传递。后来看了一些代码,慢慢感觉到用全局变量,与其说是数据的传递,更准确的不如说是怎么将数据保存下来。至于如何将数据传递过去是另一件事情。所以,整个代码中,全局变量存在的意义在于保存数据。如果是这样,我们完全可以使用局部静态变量来保存。然后,通过函数调用将保存的数据,传递给需要的地方(因为函数调用,难免影响效率。对于效率比较高的地方可以用宏定义来实现)。在这个过程中,也感觉到,这样做更好的是,可以减少对于使用变量的关心,减少需要理解的代码(好的函数名是前提),提高程序的可读性,方便以后的修改。

所以,这里我很赞同“全局变量与局部变量的本质差异,也就是生命周期的差异”。既然,静态局部变量和全局变量有相同的生命周期(对于外部来说,可见程度是不一样的),那么我们应该可以最大程度的使用静态局部变量,取代全局变量,通过函数或者宏定义来获取。当然,完全的取代,应该不容易做到。

操作系统的使用在以前的一篇博文里列了自己的看法,操作系统提供一个虚拟的并行,可以让我们将更多的精力focus on我们需要实现的功能上。当然,有些时候为了达到我们需要的效果,对于操作系统的调度算法要有一定了解(虚拟并行毕竟不是真正的并行)。

现在操作系统越来越稳定,在操作系统上的工作量也会越来越少,而应用程序的复杂度却越来越高,如何应用操作系统,实现我们需要的功能。

应用程序构建的三种方法(摘自《UNIX网络编程——进程间通信》):

1)用一个庞大的程序完成全部工作。程序的各个部分可以实现为函数,函数之间通过参数,返回值和全局变量来交换信息。

2)使用多个程序,程序之间使用某种形式的IPC进行通信。(因为彼此间运行地址空间独立,可以理解为多个MCU协同工作)

3)使用一个包含多个线程的程序,线程之间使用某种IPC。(因为彼此间运行地址空间共享,可以理解为一个MCU执行多个任务)

 

关于静态局部变量初始化与未初始化占用,执行文件大小的验证代码:

20131223212547830.jpg

 

初始化静态变量a

20131223212600585.jpg

 

未初始化静态变量a

20131223212609947.jpg

 

关于MCU使用操作系统的一点看法:

http://forum.eet-cn.com/BLOG_ARTICLE_18771.HTM

c

文章评论2条评论)

登录后参与讨论

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 的方式, 使得静态局部变量在函数访问的方式下, 显得数据传递更规范, 更不易被误用与增加可读性...略略考虑下, 我不持反对意见, 但这将导致代码的工作量增加... 当然, 或者反过来说, 在大型团队项目中, 集体代码工作量减少吧?

相关推荐阅读
catch2000 2015-07-19 11:44
信号线小电阻的作用
在一块新的PCB上,测试系统能否正常运行的时候,发现系统上电后没有正常启动。  系统框图如下:   在上电的时刻,CPU A(GPIO电平2.6V)会向串口发送启动日志数据,CPU A启动后,...
catch2000 2015-07-05 17:04
协议设计中ACK机制的影响
在TCP/IP中,延时ACK和Nagle算法。  TCP为了同时处理成块数据(通常为512字节的用户数据)和交互数据(通常用户数据比较少,例如不大于10个字节),采用了延时ACK和Nagle算法...
catch2000 2015-05-23 15:48
话说物联网操作系统
最近好多家都宣布推出自己的物联网操作系统。   1. Google将要在Google I/O大会发布的Brillo; 2. 三星推出的Artik芯片搭载Mentor Graphics的...
catch2000 2015-03-31 23:52
不要采用异或来交换两个变量
在进行两个变量的时候,经常会看到有些书误人子弟的推荐使用异或的方式: 方式一 {   x = x ^ y;   y = x ^ y;   x = x ^ y; } 而不是...
catch2000 2014-10-09 07:28
为什么要测试先行
在产品的研发过程中,测试一项至关重要。不论是软件还是硬件。   软件的测试先行,在研发过程中,就做到质量的保证,因为在出现Bug的时候,容易定位Bug,而且即使是在客户端出现Bug,也能够...
catch2000 2014-10-09 07:26
C语言的面向对象编程(一)
一、前言 对于编程而言,重要的是解决问题的方式,而不是语言本身。面向对象与面向过程是解决问题和思考问题的方式。C语言常说是面向过程开发的语言,因为其缺少很多对于面向对象特性的支持。但,这并不影...
我要评论
2
18
关闭 站长推荐上一条 /2 下一条