C/C++中static关键字总结

Posted by WSY on September 6, 2019

概述

  static关键字在C/C++语言中比较常用,使用恰当能够大大提高程序的模块化特性,有利于扩展和维护。本文总结一下C/C++中static的常见用法,仅供参考,若有错漏之处,敬请指正。

static 修饰变量

局部变量

  • 普通局部变量存储于栈空间中,函数执行完毕会立即释放。使用static关键字修饰的静态局部变量存储于进程的全局数据区,即使函数返回,它的值也会保持不变。
  • 编译器一般不对普通局部变量进行初始化,也就是说它的值在初始时是不确定的,除非对其显式赋值。使用static关键字修饰的静态局部变量即使在声明时未对其赋初值,编译器也会把它初始化为0。

下面用一段代码解释:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#include<stdio.h>

int funOne(void)
{
    static int i = 0;   /*静态局部变量在函数第一次被调用被分配内存后(存储在全局数据区中),就算函数或者代码块执行完毕,该变量也不会被回收销毁,直到程序结束静态变量才会被回收。*/
    i++;
    return i;
}

int funTwo(void)
{
    int j = 0;			   /*每次调用函数时分配变量j的内存(存储在栈空间中),函数执行完成后释放内存*/
    j = j+2;
    return j;
}

int main()
{
    int k = 0,m = 0,n = 0;
    for(k = 0;k < 5;k++)
    {
        m = funOne();
        n = funTwo();
        printf("m=%d,n=%d\n",m,n);
    }
    return 0;
}

/****************运行结果****************/
m=1,n=2
m=2,n=2
m=3,n=2
m=4,n=2
m=5,n=2

全局变量

static 修饰全局变量主要作用是限制其作用域,它与全局普通变量的区别在于:

  • 普通全局变量对整个工程可见,其他文件可以使用extern外部声明后直接使用。也就是说不能在其他文件再定义一个相同名字的变量(否则编译器会认为它们是同一个变量而报错)
  • 使用 static 修饰的静态全局变量仅对当前文件可见,其他文件不能以任何形式访问此变量,且其他文件可以定义与其同名的变量,两者互不影响。
1
2
3
4
5
6
7
8
9
10
11
#include<stdio.h>

static int i;   //定义static修饰的全局变量,未初始化时会默认初始化成0,并且其他文件以任何方式都无法访问此变量
int main()
{
    printf("i=%d",i);
    return 0;
}

/****************运行结果****************/
i=0

static 修饰函数

static 修饰函数与全局变量类似,在函数的返回类型前加上static,就是静态函数。其特性如下:

  • 静态函数只能在声明它的文件中可见,其他文件不能调用该函数。
  • 其他文件可以定义相同名字函数,互相不影响。

通过一段代码详细解释:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
/*file1.c*/
#include <stdio.h>
#include "file2.h"

static void fun1(void)
{
    printf("hello from fun1.\n");
}

int main(void)
{
    fun1();
    fun2();
    return 0;
}

/*file2.c*/
#include <stdio.h>

static void fun2(void)
{
    printf("hello from fun2.\n");
}

/****************运行结果****************/
//上述程序编译会报错,因为 file2.c 中的 fun2() 被static修饰了,因此 fun2() 不能在 file1.c 中被调用,即使 file1.c 中已经包含了相关头文件。

修改 file2.c 的内容如下:

1
2
3
4
5
6
7
8
9
10
#include <stdio.h>

void fun2(void)     //修改为普通函数
{
    printf("hello from fun2.\n");
}

/****************运行结果****************/
hello from fun1.
hello from fun2.

面向对象

修饰静态数据成员

  可以使用 static 关键字把类数据成员定义为静态的。当我们声明类的成员为静态时,这意味着无论创建多少个类的对象,静态成员都只有一个副本。对静态数据成员的定义与使用应注意:

  • 静态数据成员的访问属性同普通数据成员一样,可以为public,protected,private。
  • 静态数据成员脱离具体对象而独立存在,其存储空间是独立分配的,不是任何对象存储空间的一部分,但逻辑上所有的对象都共享这一存储单元,所以对静态数据成员的任何操作都将影响共享这一存储单元的所有对象。
  • 静态数据成员是一种特殊的数据成员,它表示类属性,而不是某个对象单独的属性,它在程序开始产生,在程序结束时消失。
  • 静态数据成员应在类体外进行初始化(静态数据成员的初始化与它的访问控制权限无关)。
  • 静态数据成员初始化时前面不加 static 关键字,以免与一般静态变量或对象混淆。由于静态数据成员是类的成员,因此在初始化时必须使用类作用域运算符::限定它所属的类

下面通过一个实例具体说明:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#include <iostream>
using namespace std;

class Box
{
   public:
      static int objectCount;
      // 构造函数定义
      Box(double l=2.0, double b=2.0, double h=2.0)
      {
         cout <<"Constructor called." << endl;
         length = l;
         breadth = b;
         height = h;
         // 每次创建对象时增加 1
         objectCount++;
      }
      double Volume()
      {
         return length * breadth * height;
      }
   private:
      double length;     // 长度
      double breadth;    // 宽度
      double height;     // 高度
};

// 初始化类 Box 的静态成员
int Box::objectCount = 0;

int main(void)
{
   Box Box1(3.3, 1.2, 1.5);    // 声明 box1
   Box Box2(8.5, 6.0, 2.0);    // 声明 box2

   // 输出对象的总数
   cout << "Total objects: " << Box::objectCount << endl;

   return 0;
}

/****************运行结果****************/
Constructor called.
Constructor called.
Total objects: 2

修饰静态成员函数

静态数据成员为类属性,在定义类后、建立对象前就存在。因此,在建立对象前不能通过成员函数存取静态数据成员。C++提供了静态成员函数,用来存取类的静态成员。

  静态成员函数是用关键字static声明的成员函数,它属于整个类而不属于类中的某个对象,是该类的所有对象共享的成员函数。静态成员函数可以在类体内定义。也可以在类内声明为static,在类外定义。当在类外定义时,不能再使用static关键字作为前缀。静态函数成员的调用形式有如下两种:

  • 通过类名调用静态成员函数
  • 通过对象调用静态成员函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
#include <iostream>
using namespace std;

class Box
{
   public:
      static int objectCount;
      // 构造函数定义
      Box(double l=2.0, double b=2.0, double h=2.0)
      {
         cout <<"Constructor called." << endl;
         length = l;
         breadth = b;
         height = h;
         // 每次创建对象时增加 1
         objectCount++;
      }
      double Volume()
      {
         return length * breadth * height;
      }
      static int getCount()
      {
         return objectCount;
      }
   private:
      double length;     // 长度
      double breadth;    // 宽度
      double height;     // 高度
};

// 初始化类 Box 的静态成员
int Box::objectCount = 0;

int main(void)
{

   // 在创建对象之前输出对象的总数
   cout << "Inital Stage Count: " << Box::getCount() << endl;

   Box Box1(3.3, 1.2, 1.5);    // 声明 box1
   Box Box2(8.5, 6.0, 2.0);    // 声明 box2

   // 在创建对象之后输出对象的总数
   cout << "Final Stage Count: " << Box::getCount() << endl;
   cout << "Final Stage Count: " << Box1.getCount() << endl;
   cout << "Final Stage Count: " << Box2.getCount() << endl;
   return 0;
}
/****************运行结果****************/
Inital Stage Count: 0
Constructor called.
Constructor called.
Final Stage Count: 2
Final Stage Count: 2
Final Stage Count: 2