C语言中关键字static
在C语言中,static是修饰变量和函数的。
常见的有以下几种修饰类型:
1.修饰局部变量—称为静态局部变量。
2.修饰全局变量—称为静态全局变量。
3.修饰函数—称为静态函数。
4.静态成员函数(C++)
首先我们来讨论一下当static修饰局部变量时,举个例子:
#include<stdio.h>
void test(){
int a = 1;
a++;
printf("%d\n, a");
}
int main(){
int i = 0;
while(i < 10){
test();
i++;
}
return 0;
}
OK,首先我们看一下当没有static修饰时我们的代码运行输出结果应为10个2。
#include<stdio.h>
void test(){
//加static修饰:
static int a = 1;
a++;
printf("%d\n, a");
}
int main(){
int i = 0;
while(i < 10){
test();
i++;
}
return 0;
}
这时候当我们在int a前面加static,这时候我们的程序运行打印输出结果是多少呢?答案是2~11。
为什么答案是2~11呢? 因为当我们加了static修饰局部变量时会使原本局部变量生命周期停滞,不再销毁,保留了上一次运行时的结果。本质上,当static修饰局部变量时,它会改变变量的存储位置,当存储位置的改变,会影响变量的生命周期,让生命周期变长与整个程序的生命周期一致。
所以当这里程序运行是i = 0 < 10满足循环条件进入循环之后进入函数内部打印出第一次a的值为2;i++程序再次运行进入循环调用函数,函数保留了上次a的值也就是2,所以这里打印输出的结果为a + 1也就是3;继续循环当i = 2时函数内部的值应为4。以此类推当i = 9时我们的输出结果循环10次a的值应为11,所以程序最后打印结果为2~11
如果局部变量明白了我们再来讨论一下当static修饰全局变量的情况:
首先我们先思考一下它的结构;我们先定义一个test1.c的文件,我们在这个文件里面定义一个static int a = 10;然后我们在同一个文件里面时可以访问这个变量的。我们再创建另一个文件test2.c的文件,我们加个extern声明也就是 extern int a试图访问test.1中的变量;这时候编译器会报错并提示未声明a的变量,因为static限制了外部文件的链接。
例如:我们先创建test1.c文件:
//test1.c
#include<stdio.h>
//定义static全局变量
static int a = 10;
void print(){
printf("test1.c: a = %d ", a);
}
void count_Var(int value){
a = value;
}
int main(){
//打印初始值
print();//输出test1.c: a = 10;
//修改static修饰后的全局变量
count_Var(20);
//再次打印修改后a的值
print();//输出test1.c:a = 20;
return 0;
}
再创建test2.c文件加extern 试图访问test1.c中的变量
//test2.c
#include<stdio.h>
//试图访问test1.c中a的值(会报错!)
extern int a;//链接错误,找不到test1.c中a的值
void printFromTest(){
printf("test2.c: a = %d ", a);//编译失败
}
int main(){
printfFromTest();
return 0;
}
我们知道全局变量具有外部链接属性,当我们在其它文件中访问变量时加extern声明就可以被访问即编译+链接—>可执行程序。而当我们加上static修饰全局变量的时候这个全局变量的外部链接属性就变成了内部链接属性(作用域变小);其它源文件.c就访问不到这个全局变量了;test2.c文件中即使加了extern声明也无法访问test1.c中的变量。
我们再来讨论一下static修饰函数的情况:
static修饰函数时其实和static修饰全局变量情况类似,当static修饰函数时,它限制了函数的作用域仅限于当前的源文件,类似于全局变量的内部链接属性。说明其他源文件无法通过函数名调用这个被static修饰的函数。我们按照全局变量的方法来创建test1.c , test2.c.
//test1.c
#include<stdio.h>
//定义一个static函数(这个函数只能在test1.c源文件内调用)
static void Function(){
printf("Today is Friday in test1.c");
}
//普通函数(可被其它文件调用)
void sortArray(){
printf("Today is Friday in test1.c");
Function();//只能在本文件中被调用
}
再来创建test2.c文件
#inciude<stdio.h>
//声明test1.c中的函数
void sortArray();
int main(){
//正确调用函数
sortArray();
//错误调用函数
Function();//会报错!因为sortArray在test1.c文件中并且被static修饰,无法调用
return 0;
}
当程序运行起来可以发现我们的程序报错因为sortArray在test1.c文件中被static修饰,无法通过函数名调用,程序只会执行sortArray函数并打印内容。简单来说在C语言中,static修饰函数时,表示该函数具有内部链接属性,即只能在它定义的源文件内被调用,其它源文件无法直接通过函数名直接访问。
最后说一下静态成员函数:
在C语言中,结构体本身不支持成员函数我们先通过C++中的结构体和类来实现静态成员函数
静态成员变量在结构体中定义,当我们定义static成员变量时,该变量属于整个结构体类型(而非单个实例),所有实例共享该变量。
#include<iostream>
using namespace std;
struct Counter{
static int count;//静态成员变量声明
Counter(){
count++;
}
};
int Counter::count = 0;//静态成员变量定义和初始化
int main(){
Counter c1, c2, c3;
cout << "Count: " << Counter::count << endl;
return 0;
}
如果要实现在C语言中实现静态成员函数我们可以通过静态成员变量来实现
在结构体外定义一个static(全局变量)并通过结构体类型名关联
#include<stdio.h>
//定义结构体
struct Counter{
int value; // 普通成员变量
};
//定义静态全局变量
static int ToCount = 0;
//构造函数
void InitCounter(struct Counter* c){
c->value = 0;
ToCount++;
}
//获取总实例数(静态成员变量访问接口)
int castCounter(){
return ToCount;
}
int main(){
struct Counter c1, c2;
InitCounter(&c1);
InitCounter(&c2);
printf("ToCounters: %d", castCounter());//打印输出ToCounters:2
return 0;
}
静态全局变量为ToCount所有Counter实例共享该变量,通过castCounter()函数提供访问接口
总结:
I:用于声明静态局部变量时,静态局部变量在函数第一次调用时分配内存空间,函数结束时不会释放内存空间,下次调用时,静态局部变量的值会保留上次调用结束时的值。
实际应用场景:1.统计函数被调用次数,维护函数调用状态。
2.模块化设计:在函数内部保存私有状态,避免使用全局变量。
II:用于声明静态全局变量时,静态全局变量只能被它存在的文件的使用,无法被其它文件访问。
实际应用场景:1.隐藏模块内部状态(如单例模式)
2.避免全局命名冲突
III:用于声明静态函数时,静态函数只能在它被声明的文件中调用,其它文件无法通过声明直接调用。
实际应用场景:数学模块计算,一些函数仅供内部使用。
IV:用于静态成员函数时,静态成员变量由所有类的对象共享,不依赖于具体对象的存在,无论创建多少个对象,静态成员变量只有一份拷贝。静态成员变量在程序启动时分配内存,在程序结束时释放。
实际应用场景:统计对象数量
共享配置参数