[关闭]
@CrazyHenry 2018-02-26T16:47:48.000000Z 字数 3119 阅读 1068

0.x 4.static修饰符

ccccC++Primer


一篇好文

image.png-339.7kB
image.png-133.3kB
image.png-166.4kB

image.png-175.3kB

函数调用过程

image.png-176.1kB

可见,外部函数的调用过程会反复建立堆栈框架,不同的位置建立不同的堆栈体,因此看起来像是"每个函数调用点维护函数的一份拷贝"。而内部函数(静态函数)把函数放在一个固定的静态区,不需要反复建立调用堆栈框架,因此看起来只有一份函数一样。
但实际上,函数的定义始终只有一份,在代码段存储,需要处理的数据存在数据段,然后代码段的一条条指令依次执行,处理数据段的数据。只不过数据段有些是静态数据,不需建立堆栈空间,有些是栈空间,用完就会销毁,下一次调用需要重新建立!

个人博文:

首先要理解生存周期与作用域的区别:

生存周期: 变量从定义到销毁的时间范围。存放在全局数据区的变量的生存周期存在于整个程序运行期间,而存放在栈中的数据则随着函数等的作用域结束导致出栈而销毁,除了静态变量之外的局部变量都存放于栈中。

作用域: 变量的可见代码域(块作用域,函数作用域,类作用域,程序全局作用域)。

static变量是指静态的变量,不管是在全局还是局部声明的static变量都存放于程序的全局变量区域,所以它的生命周期是从程序开始到程序结束。但是static变量的作用域并不等同于它的生存周期,它的作用域决定于它被定义的位置。可以认为static变量的作用域<=生存周期。

举一个局部声明的例子。在函数test中声明静态变量i:

  1. void test()
  2. {
  3. int m=3;
  4. static int i=5;
  5. }

局部变量m存放在栈中,当test函数结束,m将被销毁;静态变量i不存放在栈中,而是存放于程序的全局变量区域,因此随着函数test的结束,它并不随着出栈操作而被销毁,它的生存周期存在于程序的整个运行期;然而m和i的作用域都仅存在于test函数中它们的定义之后,即test调用结束之后,m和i就不再可用,但是i仍存在于内存之中。

再举一个全局声明的例子。在文件A 中定义静态变量j:

  1. int n=3; //默认为extern
  2. static int j=5; //声明为static

全局变量和静态变量j都存放于程序的全局数据区域,它们的生存周期都是程序的整个运行期,但是n的作用域为全局作用域,可以通过extern在其他文件中使用,而j只能在文件A中使用,例如在文件B中:

  1. extern int n; //ok
  2. extern int j; //error: j在文件B中不可见
  3. int a=n;//ok:但这里有个初始化先后的问题,具体参见参考一
  4. int b=j;//error

也就是说,在声明全局的static变量时,static没有改变它的生存周期,也即存储位置(因为全局变量本来就存储在全局数据域),而是将变量的作用域限制在当前文件中。

1.static全局变量与普通的全局变量有什么区别 ?

全局变量(外部变量)的说明之前再冠以static 就构成了静态的全局变量。

全局变量本身就是静态存储方式,静态全局变量当然也是静态存储方式。 这两者在存储方式上并无不同。

这两者的区别在于非静态全局变量的作用域是整个源程序, 当一个源程序由多个源文件组成时,非静态的全局变量在各个源文件中都是有效的。 而静态全局变量则限制了其作用域, 即只在定义该变量的源文件内有效, 在同一源程序的其它源文件中不能使用它。由于静态全局变量的作用域局限于一个源文件内,只能为该源文件内的函数公用,因此可以避免在其它源文件中引起错误。

static全局变量只初使化一次,防止在其他文件单元中被引用 

2.static局部变量和普通局部变量有什么区别 ?

  把局部变量改变为静态变量后是改变了它的存储方式即改变了它的生存期。把全局变量改变为静态变量后是改变了它的作用域,限制了它的使用范围。

  static局部变量只被初始化一次,下一次依据上一次结果值;  

*3.static函数与普通函数有什么区别?

只在当前源文件中使用的函数可以声明并定义为内部函数(static修饰的函数),内部函数应该在当前源文件中定义。对于可在当前源文件以外使用的函数,应该在一个头文件中声明,函数的实现写在另一个源文件内,要使用这些函数的源文件只要包含头文件即可,函数实现的源文件不需要包含!

static函数与普通函数作用域不同,仅在本源文件能正常访问(头文件里定义的static函数只要被include了就可以使用)。一般不在头文件里定义静态函数/变量。

static函数在内存中只有一份,普通函数在每个被调用中维持一份拷贝。

4.C++11标准下,全局静态变量的声明应该使用namespace

  1. //需要一个全局静态变量
  2. //file.h
  3. namespace
  4. {
  5. int i;//全局静态变量,被初始化为0,生命周期为整个程序,作用域为本文件全局作用域
  6. }
  7. int func()
  8. {
  9. i = 20;//使用外层::i
  10. cout<<i<<endl;
  11. static int i;
  12. cout<<i<<endl;
  13. //局部静态变量,被初始化为0,覆盖外层的::i,生命周期为整个程序,作用域为当前函数体内
  14. }

总结

1.静态存储区存三种变量:
全局非静态变量,全局静态变量,局部静态变量
2.全局非静态变量默认是extern的,具体参见extern的博文

3.为了理解静态函数和静态变量,可以参考以下程序:

  1. //test1.h
  2. #include <iostream>
  3. int func1();
  4. //static int si = 80;//在头文件定义静态变量/函数是没有意义的
  5. //test.cpp
  6. #include "test1.h"
  7. #include <iostream>
  8. using namespace std;
  9. int i = 180;//全局变量,可以在其他文件访问
  10. static int sii = 50;//全局静态变量sii,只在本文件可访问
  11. static int func2();//静态函数只在使用它的源文件声明和定义
  12. int func1()
  13. {
  14. cout<<"调用普通函数"<<endl;
  15. func2();//使用静态函数
  16. }
  17. int func2()
  18. {
  19. cout<<"调用静态函数"<<endl;
  20. }
  21. //主函数main.cpp
  22. #include <iostream>
  23. #include "test1.h"//包含该头文件,其中就包含了func1的声明
  24. //注意:这里include了两次iostream,但是由于标准库的良好代码风格,防卫式声明,因此只会#include一次<iostream>
  25. using namespace std;
  26. extern int i;//声明这是一个全局变量,将在另外一个文件定义
  27. int sii = 88; //这里的全局变量可以和test.cpp的全局静态变量重名,不会冲突。
  28. int func2()//这里的函数可以和test.cpp的静态函数重名,不会冲突。
  29. {
  30. cout<<"另一个文件的同名函数"<<endl;
  31. }
  32. int main()
  33. {
  34. cout<<i<<endl;//180
  35. //cout<<si<<endl;//在头文件定义的全局变量,可以在这里访问,但是没有什么意义
  36. cout<<sii<<endl;//这里的sii=88,不会调用test.cpp的静态变量
  37. func1();//func1可以直接调用外部test.cpp的
  38. func2();
  39. //func2调用不到test.cpp的静态函数,只能调用前边定义的那个"另一个文件的同名函数"
  40. return 0;
  41. }

image.png-21.3kB

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注