C语言的static用法
关键字“static”,译成中文就是“静态的”。C语言中static关键字是用来修饰变量和函数的,其作用是限定变量的生命周期(物理上改变变量存储区域),限定变量和函数的作用域。
(这里插图一张内存区域分布图方便下面的概念了解)
1. 修饰局部变量
局部变量被static修饰后变为静态局部变量
由内存区域分布图可以看到静态变量存储在内存的常量区;
因此静态局部变量特性:
- 该变量生命周期变为全局
- 作用域不变
例1:
#include<stdio.h>
void test_1()
{
int a = 0;
a++;
printf("%d ", a);
}
void test_2()
{
static int a = 0;
a++;
printf("%d ", a);
}
int main()
{
printf("普通变量:");
for (int i = 0; i < 5; i++)
{
test_1();
}
printf("\n静态变量:");
for (int i = 0; i < 5; i++)
{
test_2();
}
return 0;
}
为什么 test_1()
与 test_2()
仅仅只是内部定义的变量又无关键字static,结果就截然不同?
在
test_1()
函数中,变量a 为普通变量,在函数栈帧上开辟。
随着函数每被调用一次,普通变量a都会重新执行一遍int a = 0
;
因此,main函数重复调用该函数5次,结果重复输出5次;
在
test_2()
函数中,变量a 为静态变量,在函数全局数据区上开辟。
静态变量a在第一次函数被调用时定义并初始化,之后函数test_2()
调用时静态变量a不在重新定义;
且变量a生命周期变为全局,不随函数test_2()
栈帧的释放而消失。
因此,main函数重复调用该函数5次,每次变量a的值都会累加。
2. 修饰全局变量
全局变量被static修饰后变为静态全局变量
由内存区域分布图可以看到全局变量与静态变量都存储在内存的常量区;
因此静态全局变量特性:
- 作用域修改,该变量只能在本文件内使用(不能跨文件调用,extern声明也不行)
- 生命周期不变仍为全局
3. 修饰函数
函数被static修饰后变为静态函数
静态函数特性:
作用域改变,该函数只在本文件有效(不能跨文件调用,extern声明也不行)
静态函数优点
- 函数的定义和声明默认情况下是extern的,但静态函数只是在声明他的文件当中可见,不能被其他文件所用;
- 其他文件中可以定义相同名字的函数,不会发生冲突;
C++的static用法
C++语言中对关键字static的定义不但完全包含了C语言static的功能,还增添了新的定义:修饰类中的成员变量与方法。
1. 修饰成员变量
类中成员变量被static修饰后变为静态成员变量
,
一个类中可以有一个或多个静态成员变量,所有的对象都共享这些静态成员变量,都可引用。
静态成员变量的定义
-
静态成员变量在类中只是声明,必须在类外进行初始化(不用加static)
初始化时可以赋初值,也可以不赋值。
如果不赋值,那么会被默认初始化为 0。
全局数据区的变量都有默认的初始值 0,而动态数据区(堆区、栈区)变量的默认值是不确定的 -
静态成员变量也不能在初始化列表初始化,初始化列表是用来专门初始化对象成员
静态成员变量在类外的访问
- 类名::静态成员变量名字(常用)
- 对象.静态成员变量名字
- 上述的两种方法读到的静态成员变量都是同一个
例2:静态成员变量的定义使用
#include<iostream>
using namespace std;
class Test
{
public:
static int a; //静态成员变量的类内声明
int b; //普通成员变量
};
int Test::a = 0; //静态成员变量的类外定义
int main()
{
cout << Test::a << endl;
return 0;
}
静态成员变量的特性:
-
静态成员变量(函数)只与类有关,与所有对象无关
static 成员变量和普通 static 变量一样,都在内存分区中的全局数据区分配内存,到程序结束时才释放。
这就意味着,static 成员变量不随对象的创建而分配内存,也不随对象的销毁而释放内存。
而普通成员变量在对象创建时分配内存,在对象销毁时释放内存。
-
静态成员变量不影响类大小(静态成员变量类外存储,是命名空间属于类的全局变量,存储在 data 区)
-
静态成员变量属于类成员,仍受访问限定符(public、protected、private)的限制
#include<iostream>
using namespace std;
class Test_1
{
public:
static int a; //静态成员变量的类内声明
int b; //普通成员变量
};
int Test_1::a = 0; //静态成员变量的类外定义
class Test_2
{
public:
int a; //普通成员变量
int b; //普通成员变量
};
int main()
{
//cout << Test::a << endl;
cout << "有静态成员变量的类大小:"<<sizeof(Test_1) << endl;
cout << "替换为普通成员变量的类大小:" << sizeof(Test_2) << endl;
return 0;
}
2. 修饰成员函数
类中成员函数被static修饰后变为静态成员函数
静态成员函数的定义与使用
- 直接在类中成员函数前加static即可修饰
- 类外调用格式:类名::静态成员函数名
静态成员函数的特性:
-
静态成员函数只与类有关,不属于任何对象
-
静态成员函数依然是类的成员函数,仍受访问限定符(public、protected、private)的限制
-
静态成员函数与对象无关,因为其函数参数不含隐藏this指针
静态成员函数与普通成员函数的根本区别
编译器在编译一个普通成员函数时,会隐式地增加一个形参 this,并把当前对象的地址赋值给 this,所以普通成员函数只能在创建对象后通过对象来调用,因为它需要当前对象的地址。
而静态成员函数可以通过类来直接调用,编译器不会为它增加形参 this,它不需要当前对象的地址,所以不管有没有创建对象,都可以调用静态成员函数。
例1:
-
静态成员函数无法调用对象内部成员变量,只能访问静态成员变量
普通成员函数有 this 指针,可以访问类中的任意成员;
而静态成员函数没有 this 指针,只能访问静态成员(包括静态成员变量和静态成员函数)例2:
-
静态成员函数不能调用普通成员函数,但可以被其他普通函数调用;静态成员函数可以调用静态成员函数
普通成员函数必须通过对象才能调用,而静态成员函数没有 this 指针,无法在函数体内部访问某个对象,所以不能调用普通成员函数,只能调用静态成员函数。
例2:
-
静态成员函数无法修饰为const函数(不允许也没意义)
静态成员函数没有 this 指针,const本质上修饰的是this指针
例3: