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

C语言中的static 详细分析-----发现讲的很好所以转帖  

[复制链接]
安臣 发布时间:2015-11-17 16:39
本帖最后由 安臣 于 2015-11-17 16:40 编辑
6 W! G9 N* i& b* J6 W- C! K
" k# v9 g% f- c- I# B     对于我这样的C语言新手来说这个知识点 今天才真正弄明白。
& q5 }/ w1 i& Q& |( u& c7 a
3 V. z) {$ ^" |8 R/ U    弄懂static 对于写大型的c语言程序还是有很大的帮助。
4 s( W  J) n/ j( X) s
7 x+ j9 y9 E- u/ j6 \( W2 g% L$ j! U     原文地址:' I) T7 d. ^& l+ `5 Z  k
     http://blog.csdn.net/keyeagle/article/details/6708077
7 F2 ?% W/ P8 q1 H5 ?% f; u. W5 E3 c/ d1 ?: X8 k5 e& ?

      google了近三页的关于C语言中static的内容,发现可用的信息很少,要么长篇大论不知所云要么在关键之处几个字略过,对于想挖掘底层原理的初学者来说参考性不是很大。所以,我这篇博文博采众家之长,把互联网上的资料整合归类,并亲手编写程序验证之。

         C语言代码是以文件为单位来组织的,在一个源程序的所有源文件中,一个外部变量(注意不是局部变量)或者函数只能在一个源程序中定义一次,如果有重复定义的话编译器就会报错。伴随着不同源文件变量和函数之间的相互引用以及相互独立的关系,产生了extern和static关键字。

        下面,详细分析一下static关键字在编写程序时有的三大类用法:

        一,static全局变量

           我们知道,一个进程在内存中的布局如图1所示:

      其中.text段保存进程所执行的程序二进制文件,.data段保存进程所有的已初始化的全局变量,.bss段保存进程未初始化的全局变量(其他段中还有很多乱七八糟的段,暂且不表)。在进程的整个生命周期中,.data段和.bss段内的数据时跟整个进程同生共死的,也就是在进程结束之后这些数据才会寿终就寝。

     当一个进程的全局变量被声明为static之后,它的中文名叫静态全局变量。静态全局变量和其他的全局变量的存储地点并没有区别,都是在.data段(已初始化)或者.bss段(未初始化)内,但是它只在定义它的源文件内有效,其他源文件无法访问它。所以,普通全局变量穿上static外衣后,它就变成了新娘,已心有所属,只能被定义它的源文件(新郎)中的变量或函数访问。

以下是一些示例程序

' Q" Y1 }  X7 a3 T' [( R

file1.h如下:

  1. #include <stdio.h>
    9 i' t+ s7 r0 j# O* D/ N/ B
  2. 1 M4 L# c$ f0 O3 ^7 I8 @6 f  G
  3. void printStr();
复制代码

, |5 y# d7 T4 a, G

我们在file1.c中定义一个静态全局变量hello, 供file1.c中的函数printStr访问.

  1. #include "file1.h"6 R2 ?3 u0 y  \6 d

  2. # ]# r7 T; i( \4 G# {3 z
  3. static char* hello = "hello cobing!";- t$ r; C0 B* [3 Z" r* f% m
  4. / E, h7 r& a9 x/ ]4 P9 P
  5. void printStr()* |$ `  L4 d( Y$ Q9 q
  6. {9 W# E; K; [0 d- \, h! I
  7.         printf("%s\n", hello);0 B4 v, r! N& z! {% z
  8. }8 ~: h1 h) u* r" T) ]5 B+ A
复制代码

file2.c是我们的主程序所在文件,file2.c中如果引用hello会编译出错

  1. #include "file1.h"
    3 @( n1 Y0 _7 V4 r  L
  2. . u) a! F0 }& _% q; y( n
  3. int main()
    4 ]5 P! C' ]# D0 |+ B
  4. {
    3 I" o6 O( e0 R4 A' W( J7 ^
  5.         printStr();
    8 z% @, W; e+ r3 t9 J+ l
  6.         printf("%s\n", hello);
    / U, X5 w" `" Y0 Q: l+ t  c
  7.         return 0;
    1 j) v' E& P% R+ f  y0 x
  8. }
复制代码

报错如下:

[liujx@server235 static]$ gcc -Wall file2.c file1.c -o file29 O: ?8 ]) _+ Y$ q( l8 h1 e
file2.c: In function ‘main’:% n  l; g; Q* X, c# @! P  Q
file2.c:6: 错误:‘hello’ 未声明 (在此函数内第一次使用)
0 A1 N9 N& o) s# bfile2.c:6: 错误:(即使在一个函数内多次出现,每个未声明的标识符在其7 L) w$ g7 H, I6 \" d0 q
file2.c:6: 错误:所在的函数内只报告一次。)


- F2 f% H: O- Q) b

如果我们将file2.c改为下面的形式:

  1. #include "file1.h"
    % J! J$ m7 j" L( @; V) z* G
  2. ' E* j; E0 w; w# t
  3. int main()
    - X0 L$ R2 ~( ~) ]0 x
  4. {
    % t* p+ ~3 m" d* z. i! D
  5.         printStr();
    8 Q0 F2 t/ h  t
  6.         return 0;
    1 J2 F3 K" e! ]" `# ^5 g: j- S( J
  7. }
    $ z' p$ o% M" G& i% r; E. A
复制代码

则会顺利编译连接。

运行程序后的结果如下:
/ m8 E+ N  E! u; S[liujx@server235 static]$ gcc -Wall file2.c file1.c -o file2
" u- I. ~( m+ ~0 N* m3 V[liujx@server235 static]$ ./file2% q5 ^6 y+ L1 n' b2 E
hello cobing!
9 F9 d/ v- u: y/ r' \5 s. i3 ], J, C1 c" _, P8 Q/ j9 ]' y+ Z. F5 M; X
上面的例子中,file1.c中的hello就是一个静态全局变量,它可以被同一文件中的printStr调用,但是不能被不同源文件中的file2.c调用。


) `% D1 V; n0 x! T7 h

二,static局部变量

      普通的局部变量在栈空间上分配,这个局部变量所在的函数被多次调用时,每次调用这个局部变量在栈上的位置都不一定相同。局部变量也可以在堆上动态分配,但是记得使用完这个堆空间后要释放之。

       static局部变量中文名叫静态局部变量。它与普通的局部变量比起来有如下几个区别:

           1)位置:静态局部变量被编译器放在全局存储区.data(注意:不在.bss段内,原因见3)),所以它虽然是局部的,但是在程序的整个生命周期中存在。

           2)访问权限:静态局部变量只能被其作用域内的变量或函数访问。也就是说虽然它会在程序的整个生命周期中存在,由于它是static的,它不能被其他的函数和源文件访问。

           3)值:静态局部变量如果没有被用户初始化,则会被编译器自动赋值为0,以后每次调用静态局部变量的时候都用上次调用后的值。这个比较好理解,每次函数调用静态局部变量的时候都修改它然后离开,下次读的时候从全局存储区读出的静态局部变量就是上次修改后的值。( _' ?8 K4 L% D, d' @
以下是一些示例程序:

     file1.h的内容和上例中的相同,file1.c的内容如下:

  1. #include "file1.h"6 t$ t9 f8 M2 }% J

  2. ( J: w" j0 r7 D- j0 D7 |  \
  3. void printStr()- G, }6 t0 c1 J2 t9 ^
  4. {
    9 f0 \* |2 A8 D' s% H4 F
  5.         int normal = 0;
    3 ]+ T% _+ F; |' p/ m, Q0 E
  6.         static int stat = 0;        //this is a static local var
    7 p+ f# q3 F3 ?6 t; n8 D* _( D6 d
  7.         printf("normal = %d ---- stat = %d\n",normal, stat);+ I: T; s( S4 X
  8.         normal++;
    $ e7 |- _$ a8 ~
  9.         stat++;
    / |' w7 d5 G' Y. }+ o* E( M
  10. }
复制代码

为了便于比较,我定义了两个变量:普通局部变量normal和静态局部变量stat,它们都被赋予初值0;

file2.c中调用file1.h:

  1. #include "file1.h"
    + h: S1 i5 d% N

  2. 5 _# e3 G. B+ U! {
  3. int main()
    ; z; i9 q% R: y8 E0 o1 Q5 J
  4. {
    5 S  w3 V- t' B* T
  5. printStr();; k2 s7 A, B0 a2 S6 T  f
  6. printStr();
    # Q* [0 n2 {  S
  7. printStr();
    7 Y, t7 y, C* s  a4 `: J1 m
  8. printStr();2 c/ o- @, x; Y( S) l2 @! i& s
  9. printf("call stat in main: %d\n",stat);
    2 @5 a9 L$ y+ n: B# o
  10. return 0;& P/ \+ U# G" {1 Y; i2 E3 p  j
  11. }" A% `  b& h9 n# [2 E8 [9 L- d7 W: y
复制代码

这个调用会报错,因为file2.c中引用了file1.c中的静态局部变量stat,如下:

[liujx@server235 static]$ gcc -Wall file2.c file1.c -o file2
  A$ p6 a. t6 X7 `! h5 g6 Sfile2.c: In function ‘main’:/ I8 D5 B: B5 }- E! ~
file2.c:9: 错误:‘stat’ 未声明 (在此函数内第一次使用)1 S/ o9 H5 ^1 K' k
file2.c:9: 错误:(即使在一个函数内多次出现,每个未声明的标识符在其
7 c2 ?/ R4 @6 Q# }file2.c:9: 错误:所在的函数内只报告一次。)

编译器说stat未声明,这是因为它看不到file1.c中的stat,下面注掉这一行:

  1. #include "file1.h"( D6 g' ~+ S  b2 t

  2. ( P8 R2 K: q3 K3 v4 h# c& O! U
  3. int main()
    5 C! N1 Y6 X: C, n" L% j& G
  4. {
    5 j! j1 S5 P1 Q+ j! w4 l: ], S5 |& J
  5.         printStr();2 l) q5 {& l9 T* i: r
  6.         printStr();
    $ b$ _! F1 N3 w8 L; |
  7.         printStr();6 u' z$ g; x7 Z; h+ \. y9 _+ C+ @
  8.         printStr();
    7 q; x( e8 G. x9 j  W
  9. //        printf("call stat in main: %d\n",stat);
    0 F' y- J. s( f
  10.         return 0;) a, M4 e/ w* q- x
  11. }0 I) N, r8 q4 k$ o: y
复制代码

liujx@server235 static]$ gcc -Wall file2.c file1.c -o file21 ^4 p" u, `6 _
[liujx@server235 static]$ ./file2
5 v9 U- U9 N2 F' f4 o2 {3 Y' fnormal = 0 ---- stat = 0
0 i8 y/ ^* j6 knormal = 0 ---- stat = 1
% [  Q; i: k! ]+ J! m$ ~; G4 M- W3 lnormal = 0 ---- stat = 20 w& o7 ?# M3 v! J! x
normal = 0 ---- stat = 3
; I" E! h8 C" p运行如上所示。可以看出,函数每次被调用,普通局部变量都是重新分配,而静态局部变量保持上次调用的值不变。

需要注意的是由于static局部变量的这种特性,使得含静态局部变量的函数变得不可重入,即每次调用可能会产生不同的结果。这在多线程编程时可能会成为一种隐患。需要多加注意。

三,static函数
4 u  N' a, g7 s              相信大家还记得C++面向对象编程中的private函数,私有函数只有该类的成员变量或成员函数可以访问。在C语言中,也有“private函数”,它就是接下来要说的static函数,完成面向对象编程中private函数的功能。

            当你的程序中有很多个源文件的时候,你肯定会让某个源文件只提供一些外界需要的接口,其他的函数可能是为了实现这些接口而编写,这些其他的函数你可能并不希望被外界(非本源文件)所看到,这时候就可以用static修饰这些“其他的函数”。

           所以static函数的作用域是本源文件,把它想象为面向对象中的private函数就可以了。

下面是一些示例:

file1.h如下:

  1. #include <stdio.h>
    + H+ u- o' ~  }9 H- A& p  i

  2. ) h( D3 ?+ s: ^) ]
  3. static int called();4 ~4 f2 }& g" G8 l# z
  4. void printStr();
复制代码

file1.c如下:

  1. #include "file1.h"
    9 q# r9 j$ ~: s3 g- W2 F

  2. * K$ I3 `5 A: U* D% }0 s
  3. static int called(); i  v0 u) i) o, Q; c
  4. {) V; W: y8 z8 n2 ?5 C2 K
  5.         return 6;
    , M8 H7 ^8 s/ {2 B2 @0 f
  6. }
    6 @+ |9 W" u$ |# w  h9 t
  7. void printStr()4 s8 T7 b1 f, s. U: v; _9 c
  8. {* S! l$ r% P* E3 o, I3 U
  9.         int returnVal;
    0 E. p+ |  N4 n$ i% _% v" s% F
  10.         returnVal = called();
    ' p+ u6 d4 @/ T% s/ g7 ?: I
  11.         printf("returnVal=%d\n",returnVal);" R# b0 T3 f& c5 W
  12. }2 |: n* [9 B( Z7 Q2 x! o
复制代码
- N$ u! l2 I+ U. ]9 `

file2.c中调用file1.h中声明的两个函数,此处我们故意调用called():

  1. #include "file1.h"
    - }4 h% L5 b/ K6 W/ j
  2. 0 D3 T- }+ u7 Z! e
  3. int main()4 K! e% N( |3 e
  4. {! Z$ x5 b9 y# s( I6 h
  5.         int val;
    ( X; o/ S( _. E: `
  6.         val = called();" C7 ^! V. M4 n. s7 v7 ~9 M
  7.         printStr();
    ! x& b: u+ n. I; {) P1 Q
  8.         return 0;
    0 V3 {9 ?. f  ]& e2 `
  9. }
复制代码

编译时会报错:

[liujx@server235 static]$ gcc -Wall file2.c file1.c -o file2, l/ u6 V+ Y; x: z% R  |( o
file1.h:3: 警告:‘called’ 使用过但从未定义
+ |" E  q- ?# a, ~% A( R/tmp/ccyLuBZU.o: In function `main':( M# I) E1 r2 x& D: t3 f
file2.c.text+0x12): undefined reference to `called': K! s9 l- e, m5 {$ r6 i1 p
collect2: ld 返回 1
% l; I/ M" t& @* k6 p) a9 N; E因为引用了file1.h中的static函数,所以file2.c中提示找不到这个函数:undefined reference to 'called'

下面修改file2.c:

  1. #include "file1.h"
    9 t9 @: o1 y* @9 z* i9 p" x& Z
  2. ; W' V- Q- y9 a
  3. int main()3 M. T( R1 }: `3 \/ A$ j/ ^) V
  4. {8 v6 q& f8 y2 J0 s7 N/ s. Q
  5.         printStr();
    * ]$ p! E) M; j
  6.         return 0;" ~& j0 C* M: S0 A/ F1 G2 T
  7. }
复制代码

编译运行:

[liujx@server235 static]$ gcc -Wall file2.c file1.c -o file2
8 q. v' ?) \1 Q4 ?. D" ?0 c[liujx@server235 static]$ ./file2
8 `, l5 @) ]3 z7 I- k  i' lreturnVal=6, r8 g+ h2 A: K: s# W& B6 ?0 X

       static函数可以很好地解决不同原文件中函数同名的问题,因为一个源文件对于其他源文件中的static函数是不可见的。3 z/ |1 {/ z. t! x  N
* j6 z' D& t  H. Z% G& B


1 @; M; Y: H- J
收藏 10 评论16 发布时间:2015-11-17 16:39

举报

16个回答
?[Wait...]?& 回答时间:2015-11-17 21:41:30
必须好                        
安臣 回答时间:2015-12-10 09:19:40
nongchaoer2010 发表于 2015-12-10 09:16! I. V9 S; |( B& C3 c; H2 e
感谢楼主分享,有了更深的认识
( t' p* G: b: T, c6 L
感谢支持
nongchaoer 回答时间:2015-12-10 09:16:24
感谢楼主分享,有了更深的认识
沐紫 回答时间:2015-11-17 16:50:13
谢谢分享
creep 回答时间:2015-11-17 17:08:19
感谢分享,收藏学习!
qianfan 回答时间:2015-11-17 17:13:48
留个脚印
peter001 回答时间:2015-11-17 17:22:13
学习一下
呦、小魔 回答时间:2015-11-17 18:54:09
多谢分享
huaiqiao 回答时间:2015-11-17 19:14:28
mark一下,先收藏了。
风子 回答时间:2015-11-17 20:40:08
讲得挺全面
默默前行 回答时间:2015-11-17 21:24:15
学习了,感谢分享!
安臣 回答时间:2015-11-18 08:34:50
感谢各位
hbthgs 回答时间:2015-11-26 12:54:17
多谢分享,不错。。: `( j0 Q8 Y! y
dialman 回答时间:2015-11-27 10:35:33
谢谢分享
12下一页

所属标签

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
人形机器人运动控制、感知与智能配电
半导体创新技术与应用方向
EE架构与软件定义汽车
12V/48V 汽车智能配电(SPD)
区域控制单元(ZCU)与分区架构
关注我们
st-img 微信公众号
st-img 手机版