你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

C语言中,全局变量该怎么使用?

[复制链接]
gaosmile 发布时间:2020-3-27 10:51
01) A  h6 T8 X; n6 y  |! y
啥是全局变量

# o# N- P# B1 E3 W9 |2 z说起全局变量,就不得不提到“全局变量,局部变量,静态全局变量,静态局部变量”,这些都是编程语言中的基本概念。变量分为局部与全局,局部变量又可称之为内部变量。由某对象或某个函数所创建的变量通常都是局部变量,只能被内部引用,而无法被其它对象或函数引用。! s0 _* w1 x* c" K# H& D
全局变量既可以是某对象函数创建,也可以是在本程序任何地方创建。全局变量是可以被本程序所有对象或函数引用。
6 l! G9 Z, _& m7 l9 t$ ^2 q: C% S" ~5 E! V
从作用域看:# p; {* Q( f4 ~6 e" M# J# i5 M
全局变量具有全局作用域。全局变量只需在一个源文件中定义,就可以作用于所有的源文件。当然,其他不包括全局变量定义的源文件需要用extern关键字再次声明这个全局变量。
+ M7 B0 Q: s6 V2 X5 i: X静态局部变量具有局部作用域。它只被初始化一次,自从第一次初始化直到程序与你新内阁结束都一直存在,他和全局变量的区别在于全局变量对所有的函数都是可见的,而静态局部变量只对定义自己的函数体始终可见。
/ N, g. u8 A4 k" t局部变量也只有局部作用域,他是自动对象,他在程序运行期间不是一直存在,而是只在函数执行期间存在,函数的一次调用结束后,变量就被撤销,其所占用的内存也被收回。( I- z, O, ?3 [. }) o! |
静态全局变量也具有全局作用域,他与全局变量的区别在于如果程序包含多个文件的话,他作用于定义它的文件里,不能作用到其他文件里,即被static关键字修饰过的变量具有文件作用域。这样即使两个不同的源文件都定义了相同的静态全局变量,他们也是不同的变量。
: ^* A$ Z! H+ o1 P从分配内存空间看:0 N, W* j9 I' J  _' B7 s( ^

4 K( s' O! G% s; G全局变量、静态局部变量、静态全局变量都在静态存储区分配空间,而局部变量在栈分配空间。
- ]4 v+ @% d' _! v, H4 r2 G( t1 V0 ?' p全局变量本身就是静态存储方式,静态全局变量当然也是静态存储方式。这两者在存储方式上没有什么不同。区别在于非静态全局变量的作用域是整个源程序,当一个源程序由多个源文件组成时,非静态的全局变量在各个源文件中都是有效的。而静态全局变量则限制了其作用域,即只在定义该变量的源文件内有效,在同一源程序的其他源文件中不能使用它。由于静态全局变量的作用域局限于一个源文件内,只能为该源文件内的函数公用,因此可以避免在其他源文件中引起错误。& _$ a8 y6 w7 {$ g! P* M
1、静态变量会被放在程序的静态数据存储区里,这样可以在下一次调用的时候还可以保持原来的赋值。这一点是他与堆栈变量和堆变量的区别) z0 w" ~& Y5 O/ r: a! Q2 h
2、变量用static告知编译器,自己仅仅在变量的作用域范围内可见。这一点是他与全局变量的区别。
& m1 S! D9 E9 W/ d5 k从以上分析可以看出,把局部变量改变为静态变量后是改变了他的存储方式,即改变了他的生存期。把全局变量改变为静态变量后是改变了他的作用域,限制了他的使用范围,因此static这个说明符在不同的地方起的作用是不同的。
! P# [* o, J3 J% n4 Q8 j简单来说就是:
: Q) [$ c  W( w6 A) E' Q0 _7 f( W全局变量:在整个工程文件内都有效;“在函数外定义的变量”,即从定义变量的位置到本源文件结束都有效。由于同一文件中的所有函数都能引用全局变量的值,因此如果在一个函数中改变了全局变量的值, 就能影响到其他函数中全局变量的值。
' P# {" v  z6 T5 {$ {

6 y( j, ]# G, B& D静态全局变量:只在定义它的文件内有效,效果和全局变量一样,不过就在本文件内部;  \2 }3 H1 q; A5 W& N) B

& d( v8 z6 E) w* D7 L8 n6 ^+ ?静态局部变量:只在定义它的函数内有效,只是程序仅分配一次内存,函数返回后,该变量不会消失;静态局部变量的生存期虽然为整个工程,但是其作用仍与局部变量相同,即只能在定义该变量的函数内使用该变量。退出该函数后, 尽管该变量还继续存在,但不能使用它。      R' j6 Z5 G% ~1 g( q
: C" O% |' c0 {+ E; q! W
局部变量:在定义它的函数内有效,但是函数返回后失效。“在函数内定义的变量”,即在一个函数内部定义的变量,只在本函数范围内有效。
# s9 v+ F0 N$ n  C; @8 w- _
注意:全局变量和静态变量如果没有手工初始化,则由编译器初始化为0。局部变量的值不可知。静态局部变量全局变量最明显的区别就在于:全局变量在其定义后所有函数都能用,但是静态局部变量只能在一个函数里面用。
: K5 c3 _9 _2 M2 n! g8 V0 J通过一个代码来解释就是:. D5 ]' E0 a+ e, x
  1. 3 C. v7 U+ h2 }' T! _
  2. #include <stdio.h>
    $ w6 {$ N; \' Y, o) m0 B8 n0 a
  3. int num1 = 222;         //静态存储期5 S1 |3 ~0 ?6 ~/ w% }; r
  4. static int num2 = 111;  //静态存储期5 D) I4 u+ y( l, O9 C* q: U- T
  5. int add(int a,int b)" g0 e( o; Y/ X+ ^: w
  6. {
    # S! c$ K2 m  x" M3 I. m
  7. static int tempSum = 0;  //静态存储期
    3 Z3 j0 X0 k! `- H
  8.     tempSum = tempSum + a + b;
    7 z/ ]$ t. @$ k* @1 ~; m: Z: H
  9. return tempSum;
    6 }/ Y; {9 {+ E1 l
  10. }0 h9 G6 H$ L7 ?- c3 |
  11. int main(void)
    % ^8 i$ O1 p  {& d2 g
  12. {
    ( V% k4 ?6 M1 x( h& t
  13. printf("num1=%d,num2=%d\n",num1,num2);, R# x/ D' c4 A0 Q9 W
  14. int sum = 0;  //自动存储期
    1 [* Z2 F: H* f
  15.     sum = add(num1,num2);* R: H9 X; i2 v
  16. printf("first time sum=%d\n",sum);//sum = 333
      k" B/ e' N% j" [8 U! B
  17.     sum = add(num1,num2);
    6 `, {& {( k1 a9 `
  18. printf("second time sum=%d\n",sum); //sum = 666
    8 W' j5 h. P  T1 g+ B& ^2 C
  19. return 0;
    + q' V2 M$ {" i( |8 Y3 B
  20. }
复制代码

. a! {2 Q0 ]7 S5 Y9 P8 l- A; a$ S/ s1 ^) {! k% U( q

2 d/ x4 f, t& E4 t' {5 u

- s  e7 p7 @) D6 |7 z/ B
028 F0 {; Z; F  z. z5 Q& {
新手最容易犯的问题
嵌入式特别是单片机os-less的程序,最易范的错误是全局变量满天飞。此现象在早期汇编转型过来的程序员以及初学者中常见,这帮家伙几乎把全局变量当作函数形参来用。
) u/ @" G& k; Y4 a" V1 W' y2 U

$ |% T& _) P" J& h( V
在.h文档里面定义许多杂乱的结构体,extern一堆令人头皮发麻的全局变量,然后再这个模块里边赋值123,那个模块里边判断123分支决定做什么。  o; C- n  b1 Q; L- i
6 ?8 l( Y8 c  Q3 h8 X1 j  D% F
每当看到这种程序,我总要戚眉变脸而后拍桌怒喝。没错,就是怒喝。我不否认全局变量的重要性,但我认为要十分谨慎地使用它,滥用全局变量会引申带来其它更为严重的结构性系统问题。
! d. z- f: q0 j& V3 K) e% F( e
6 ~& _& ^- K/ J. e9 e9 b# v4 j
1. 滥用全局变量会造成不必要的常量频繁使用,特别当这个常量没有用宏定义“正名”时,代码阅读起来将万分吃力。# w" ]# X( \% s1 ^( U9 j& K9 I

* _: o& w2 r( e* \  {2 j. D$ y2. 会导致软件分层的不合理,全局变量相当于一条快捷通道,它容易使程序员模糊了“设备层”和“应用层”之间的边界。写出来的底层程序容易自作多情地关注起上层的应用。这在软件系统的构建初期的确效率很高,功能调试进度一日千里,但到了后期往往bug一堆,处处“补丁”,雷区遍布。说是度日如年举步维艰也不为过。  B7 U4 o/ w, O* v

" Q# c0 A' L. o" G# @* |% s$ w4 Y3. 由于软件的分层不合理,到了后期维护,哪怕仅是增加修改删除小功能,往往要从上到下掘地三尺地修改,涉及大多数模块,而原有的代码注释却忘了更新修改,这个时候,交给后来维护者的系统会越来越像一个“泥潭”,注释的唯一作用只是使泥潭上方再加一些迷烟瘴气。: t/ E& R/ c; v

0 [" L& P8 m+ p! B/ A; l; p' \6 c# z4. 全局变量大量使用,少不了有些变量流连忘返于中断与主回圈程序之间。这个时候如果处理不当,系统的bug就是随机出现的,无规律的,这时候初步显示出病入膏肓的特征来了,没有大牛来力挽狂澜,注定慢性死亡。
/ r) e; m/ S) h& l5 e% b7 i% q& @" U4 H. k
无需多言,您已经成功得到一个畸形的系统,它处于一个神秘的稳定状态!你看着这台机器,机器也看着你,相对无言,心中发毛。你不确定它什么时候会崩溃,也不晓得下一次投诉什么时候道理。
: c+ [  F0 z( B- Q3 r/ x
+ A  l6 p/ o% t( E& s' H# i
然后,我告诉大家现实层面的后果是什么。7 X- u' }/ p8 T. U
. C  d  q: y* t8 h
1.“老人”气昂昂,因为系统离不开他,所有“雷区”只有他了然于心。当出现紧急的bug时,只有他能够搞定。你不但不能辞退他,还要给他加薪。2. 新人见光死,但凡招聘来维护这个系统的,除了改出更多的bug外,基本上一个月内就走人,到了外面还宣扬这个公司的软件质量有够差够烂。
9 ?% f. a# x2 D  A4 {+ o3.随着产品的后续升级,几个月没有接触这个系统的原创者会发现,很多雷区他本人也忘记了,于是每次的产品升级维护周期越来越长,因为修改一个功能会冒出很多bug,而按下一个bug,会弹出其他更多的bug。在这期间,又会产生更多的全局变量。终于有一天他告诉老板,不行啦不行啦,资源不够了,ram或者flash空间太小了,升级升级。

$ X8 X( s: H/ x  @9 f' O4. 客户投诉不断,售后也快崩溃了,业务员也不敢推荐此产品了,市场份额越来越小,公司形象越来越糟糕。

) b4 J8 k' D4 \1 F6 g2 z, Y9 P$ w
03$ W- O$ n2 z- j3 L3 W
那么有什么对策?

  @; ^8 ^( u6 S' A0 J5 S1. 能不用全局变量尽量不用,我想除了系统状态和控制参数、通信处理和一些需要效率的模块,其他的基本可以靠合理的软件分层和编程技巧来解决。
$ F6 d, }$ A( {! c( c2. 如果不可避免需要用到,那能藏多深就藏多深。
, J/ ^, t. }0 ~$ M. @
1)如果只有某.c文件用,就static到该文件中,顺便把结构体定义也收进来;- q2 Q; H- g4 j3 Z' Q0 {
2)如果只有一个函数用,那就static到函数里面去;

" e: }* y) B7 T! W7 }2 ?/ g* m3)如果非要开放出去让人读取,那就用函数return出去,这样就是只读属性了;

5 v" }) Q5 @2 {4)如果非要遭人蹂躏赋值,好吧,我开放函数接口让你传参赋值;

. E1 v& p3 a% W8 I5)实在非要extern我,我还可以严格控制包含我.h档的对象,而不是放到公共的includes.h中被人围观,丢人现眼。
$ N* E# F% l" Z1 o如此,你可明白我对全局变量的感悟有多深刻。悲催的我,已经把当年那些“老人”交给我维护的那些案子加班全部重新翻写了。你能明白吗,不要让人背后唾弃你哦。

! s& x% `5 v# T
04
4 \8 |* C* [+ \  l' f) D大量使用局部变量也会容易造成栈溢出

. A6 u$ a; n% d, ~1.全局变量是不可避免要用到的,每一个设备底层几乎都需要它来记录当前状态,控制时序,起承转合。但是尽量不要用来传递参数,这个很忌讳的。9 K; m0 Z% A: C, N( b* k" b; z* ]/ ?
2.尽量把变量的作用范围控制在使用它的模块里面,如果其他模块要访问,就开个读或写函数接口出来,严格控制访问范围。这一点,C++的private属性就是这么干的。这对将来程序的调试也很有好处。C语言之所以有++版本,很大原因就是为了控制它的灵活性,要说面向对象的思想,C语言早已有之,亦可实现。
1 `+ k( f( g8 C6 U/ J6 j' L( T
3.当一个模块里面的全局变量超过3个(含)时,就用结构体包起来吧。要归0便一起归0,省得丢三落四的。
; T$ N! I+ I! T
4.在函数里面开个静态的全局变量,全局数组,是不占用栈空间的。只是有些编译器对于大块的全局数组,会放到和一般变量不同的地址区。若是在keil C51,因为是静态编译,栈爆掉了会报警,所以大可以尽情驰骋,注意交通规则就是了。
/ D* C; _5 k2 R( }
5.单片机的os-less系统中,只有栈没有堆的用法,那些默认对堆分配空间的“startup.s”,可以大胆的把堆空间干掉

0 E3 n: X0 P4 @0 t- Z- H# u6.程序模型?如何分析抽象出来呢,从哪个角度进行模型构建呢?很愿意聆听网友的意见。本人一直以来都是从两个角度分析系统,事件--状态机迁移图 和 数据流图,前者分析控制流向,完善UI,后者可知晓系统数据的缘起缘灭。这些理论,院校的《软件工程》教材都有,大家不妨借鉴下。只不过那些理论,终究是起源于大型系统软件管理的,牛刀杀鸡,还是要裁剪一下的。
3 ^7 T' c$ x- f8 b/ r. X7 W
05
, W+ `7 l, ]6 k【最后再来皮一下,不要尝试】全局变量的最佳前缀

# ]; Z* u6 t2 A0 I9 u' H  |问:全局变量的最佳前缀是什么?% J2 J3 X' U. H) W) c; w& M
0 t& x& @! c' L* x
答://
收藏 评论0 发布时间:2020-3-27 10:51

举报

0个回答

所属标签

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版