[C.C++] 在 C/C++中 static 关键字详解

65 0
Honkers 6 小时前 | 显示全部楼层 |阅读模式

static是 C/C++中的关键字之一,是常见的函数与变量(C++中还包括类)的修饰符,它常被用来控制变量的存储方式和作用范围。 在众多高级语言中都有其作为关键字或函数出现,所以这也是应当被程序员熟知其各种含义的一个单词

我们知道在函数内部定义的变量,当程序执行到它的定义处时,编译器为它在栈上分配空间,函数在栈上分配的空间在此函数执行结束时会释放掉,这样就产生了一个问题: 如果想将函数中此变量的值保存至下一次调用时,如何实现? 最容易想到的方法是定义为全局的变量,但定义一个全局变量有许多缺点,最明显的缺点是破坏了此变量的访问范围(使得在此函数中定义的变量,不仅仅只受此函数控制)。static 关键字则可以很好的解决这个问题。
另外,在 C++ 中,需要一个数据对象为整个类而非某个对象服务,同时又力求不破坏类的封装性,即要求此成员隐藏在类的内部,对外不可见时,可将其定义为静态数据。

详见C/C++内存分布

如果static变量定义时未赋初值,编译时会自动将其赋值为0

C/C++ 中的 static

这里 static 作用主要影响着变量或函数的生命周期作用域,以及存储位置

1. 静态局部变量

定义在函数内部的变量称为局部变量(Local Variable),它的作用域仅限于函数内部, 离开该函数后就是无效的

当 static 修饰局部变量时:

 ● 变量的存储区域由变为静态常量区
 ● 变量的生命周期由局部调用结束变为程序运行结束
 ● 变量的作用域不变。

函数调用开辟栈帧,函数中的局部变量在栈上分配存储空间,当函数执行完毕,函数栈帧销毁,栈空间由系统回收
而在static修饰函数局部变量的时,其修饰的静态局部变量只执行初始化一次,延长了局部变量的生命周期,直到程序运行结束以后才释放,但不改变作用域。

下面用代码进行验证:

  1. #include <stdio.h>
  2. void fun()
  3. {
  4. static int val = 0; //static 修饰局部变量
  5. val++;
  6. printf("%d\n", val);
  7. }
  8. int main()
  9. {
  10. for (int i = 0; i < 7; i++){
  11. fun();
  12. }
  13. return 0;
  14. }
复制代码

没有 static 时, 函数每调用一次, 变量就会进行一次初始化值为 0,
当由 static修饰时, 初始化语句只会被执行一次所以值会一直累加。

2. 静态全局变量

在所有函数外部定义的变量称为全局变量(Global Variable),它的作用域默认是整个程序,也就是所有的源文件,包括 .c 和 .h 文件。

当 static 修饰全局变量时:

 ● 变量的存储区域在全局数据区的静态常量区
 ● 变量的作用域由整个程序变为当前文件(extern声明也不行)
 ● 变量的生命周期不变。

一个全局变量被 static 修饰,使全局变量只能在定义变量的当前文件使用,不能在其余文件使用,即使 extern外部声明也不行。

原因: 属于文件作用域的声明在缺省的情况下为 external 链接属性, 如定义个全局变量int g_a = 1;, a的链接属性为external,而加上 static会修改变量的缺省链接属性,改为internal。
声明了全局变量 g_a 和 g_b (具有 external 链接属性 )的其他源文件在使用这两个变量时实际访问的是生命与此处的这两个变量;但是 g_c 只能由这个源文件访问,因为链接属性为internal

  1. int g_a = 1;
  2. extern int g_b;
  3. static int g_c;
复制代码

代码验证:

  1. // add.c
  2. static int global_val = 27; //static 修饰全局变量
  3. //staticdemo1.c
  4. extern global_val;
  5. int main()
  6. {
  7. printf("%d", global_val);
  8. return 0;
  9. }
复制代码

不用 static 修饰 global_val 时的结果


而使用 static 修饰时,链接时就会出现链接错误无法执行。

全局变量 与 extren

 具有 extrenal 链接属性的实体在其他语言术语中称作全局实体(global entity ),所有源文件中的函数均可以访问它。只要变量并非声明与代码块或者函数定义内部,它在缺省的情况下链接属性即为 extrenal。如果一个变量声明在代码块内部,在它面前添加 extren 关键字将使它使它所引用的是全局变量而非局部变量。
  具有链接属性为 extrenal 的实体总是具有静态存储类型。 全局变量在程序开始执行前创建,并在整个执行过程中始终存在。从属于函数的局部变量在函数在函数开始执行时进行创建,在函数执行完毕后销毁,但用于执行函数的机器指令在程序生命周期内一直存在。

使用 extren 进行声明提高代码的可读性是良好的编程习惯。

3. static 修饰函数

函数的作用域与全局变量一样都是整个程序。

当 static 修饰函数时:

 ● 函数的作用域由整个程序变为当前文件(extern声明也不行)

一个函数被 static 修饰,使函数只能在定义的源文件使用,不能在其余文件使用,即使 extern外部声明也不行。(同static 修饰全局变量)

如果我们将函数声明为 static,就会把它的链接属性从external,改为internal,这样将使得其他源文件不能访问这个函数;对于函数而言,存储类型不是问题,因为代码总是存储在只读的代码区中。

  1. // add.c
  2. static int add(int a, int b)
  3. {
  4. return a + b;
  5. }
  6. //staticdemo1.c
  7. extern add(int a, int b);
  8. int main()
  9. {
  10. printf("%d", add(10, 20));
  11. return 0;
  12. }
复制代码

这里直接看结果:

没有 static 修饰:

被 static 修饰:报了与修饰全局变量时同样的链接错误。




C++的 static 成员

 声明为static的类成员称为类的静态成员,用static修饰的成员变量,称之为静态成员变量;用static修饰的 成员函数,称之为静态成员函数

注:静态成员无法在构造函数初始化列表初始化!!


注:静态的成员变量一定要在类外进行初始化!!

const修饰的成员除外


  1. class A
  2. {
  3. public :
  4. A(){ //构造函数
  5. _count++;
  6. }
  7. A(const A& y){
  8. _count++;
  9. }
  10. static int GetCount(){ //静态成员函数
  11. return _count;
  12. }
  13. public:
  14. int a;
  15. //静态成员变量--》在类中的是声明要在类外进行定义
  16. static int _count;
  17. static const int sa = 99;
  18. const static int sb;
  19. static const int sc;
  20. };
  21. int A::_count = 0; //静态变量 _count 定义
  22. const int A::sb = 88;
  23. const int A::sc = 77;
  24. void TestA() {
  25. cout << A::GetCount() << endl; // 类静态成员通过 类名::静态成员 来访问
  26. A a1, a2;
  27. A a3(a1);
  28. cout << A::GetCount() << endl;
  29. }
复制代码
静态成员变量
  1. 静态成员变量必须在类外进行定义定义时不用加 static ,类中只是声明
const修饰的成员除外

  1. 静态成员变量为所有类对象所共享,并没有包含在具体的对象中。
    所以并不影响 sizeof() 大小

  2. 静态成员变量的访问: 类名::静态成员变量名 或 对象.静态成员变量名。

  1. cout << A::_count << endl;
  2. cout << a1._count << endl;
复制代码


类的对象可以使用静态成员函数和非静态成员函数。

注:静态成员变量也受访问限定符(public、protected、private)的限制。 所以私有的仍要通过类成员函数接口来进行访问,可以在通过类中公有的成员函数进行访问,

  1. cout << A::GetCount() << endl;
复制代码

但这种方式调用获取静态成员变量必须由静态成员函数访问,不能通过类名来调用类的非静态成员函数,否则就会出错



类外初始化的值:
下面代码的输出结果是?

静态成员函数
  1. 静态成员函数没有隐藏的 this 指针,不能访问非静态成员(变量、 函数)!


    因为静态成员函数没有隐藏的 this 指针所以也不能定义成const成员函数(const 本质就是修饰隐藏参数this )

  2. 静态成员函数不能调用非静态成员函数。

  3. 非静态成员函数可以调用静态成员函数。

  1. static void fun(){
  2. _count = 0;
  3. }
  4. int GetCount(){
  5. //cout << this << endl;
  6. fun();
  7. return _count;
  8. }
复制代码



总结:

(1) 静态成员变量使用前必须先初始化(在类外定义),如:int A::_count = 0;
(2) 静态成员变量为所有类对象所共享,也受访问限定符(public、protected、private)的限制
(3) 静态成员函不能调用非静态成员函数,非静态成员函数可以调用静态成员函数
(4) 静态成员函数没有隐藏的 this 指针
所以静态成员函数可以访问类的静态成员、不能访问类的非静态成员(静态成员函数如何访问非静态成员)




文章内容随着对C++的深入学习还有待更加完善,欢迎大佬指正




参考:
书籍:《C和指针》
https://www.cnblogs.com/33debug/p/7223869.html
https://baike.baidu.com/item/%E9%9D%99%E6%80%81%E5%87%BD%E6%95%B0/5644260

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

×
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

Honkers

特级红客

关注
  • 3239
    主题
  • 36
    粉丝
  • 0
    关注
这家伙很懒,什么都没留下!

中国红客联盟公众号

联系站长QQ:5520533

admin@chnhonker.com
Copyright © 2001-2025 Discuz Team. Powered by Discuz! X3.5 ( 粤ICP备13060014号 )|天天打卡 本站已运行