c语言static 讲解(CC入门之static是个雷)
找不到出错原因,让C 码农深感受挫;然而让他们觉得生无可恋的,还是同一段代码,时而出错,时而运行良好。 -- 题记
对于初学C /C的人来说,很容易把static修饰符同const修饰符混淆起来。字面上,一个静态的,一个不变的,意思的确相近。前面讲过const,现在有必要系统地讲解一下static,来做一个区分。
static在C 中有多层意思。最直观的理解是,如果修饰一个变量,则该变量在作用域内,只被定义一次,再定义无效。当然static还有其他涵义。
(一)再定义无效
怎么理解呢?static变量的生命周期,贯穿整个程序的运行,从被定义的地方开始,直到main函数退出为止。同一个静态变量的第二次,第三次乃至第N次定义,都不会导致重新分配内存给变量,也不会修改变量原来的值。看看C /C入门之堆和栈提到的例子:
void f(){
static int i =0;
i ;
cout << "The value of i:" << i << endl;
}
int main(){
f();
f();
}
第一次打印的值是1,第二次打印的值是2。虽然i是一个局部变量,由于有static修饰,它是被存在内存中的静态区,而不是栈中的,所以不用担心程序退出后被销毁。当第二次调用函数f的时候,编译器碰到static int i=0;的定义,它不会入栈一个新的变量i,而是到静态内存区去找到变量i。
类的静态成员适用同样的规则。
class A{
static int i;
public:
void f(){
i ;
cout << "The value of i:" << i << endl;
}
};
int A::i = 0;
int main(){
A classa;
A classb;
classa.f();
classb.f();
}
打印出来的i的值分别是1和2。另外,由于类的特殊性,类的成员一般不支持在声明的时候初始化,由static const修饰的int,float/double以及char除外。字符串,数组和结构等,都必须在类外部进行初始化,无论是否有static const或者static修饰。
(二)类的静态函数成员的局限
类的静态函数成员,只能调用类的其他静态函数,以及只能访问类的静态数据成员。它的调用方式可以是
类名::静态函数名
对象名.静态函数名或者对象名->静态函数名
(三)static用来限制变量作用域
如果在函数外定义一个变量,那么这个变量就是全局变量,再在这个变量前加static修饰符,变量的涵义发生了变化,static也不再是静态的意思了。这个时候的变量,变成了一个内部连接,不再为外部可见。static表示内部的意思,正好与extern的意义相反。也就是说,如果在另外一个文件,定义了同名变量,则两个变量没有任何关联(同名变量不能加extern关键字,否则去static变量冲突)。
另外,用extern关键字修饰局部变量也是毫无意义的。
(四)静态(全局)变量的加载顺序是个雷
为什么这么说呢?找不到出错原因,让C 码农深感受挫;然而让他们觉得生无可恋的,还是同一段代码,时而出错,时而运行良好。全局变量的加载顺序,就是一个这样雷。
全局变量定义在一个单一文件当中,它可以被外部文件引用。如果碰巧,两个定义在不同文件中的全局变量互相之间有依赖关系,而不同文件的加载顺序,是编译器不能保证的,我们将会碰到一场灾难。
试看:
//First.cpp
extern int y;
int x = y 1;
//Second.cpp
extern int x;
int y = x 1;
如果First.cpp被先加载,x的值为1,y被先初始化为0,当Second.cpp被加载的时候,y的值为2。
相反,如果Second.cpp先被加载,y的值为1,x被初始化为0,当First.cpp被加载的时候,x的值为2。
进入main之后,x和y的初始值,取决于被加载的顺序,而这个顺序,是不能被保证的!
有不少有效的办法,来避免这个情况的出现。最有效的办法还是,避免静态对象依赖的出现。
,
免责声明:本文仅代表文章作者的个人观点,与本站无关。其原创性、真实性以及文中陈述文字和内容未经本站证实,对本文以及其中全部或者部分内容文字的真实性、完整性和原创性本站不作任何保证或承诺,请读者仅作参考,并自行核实相关内容。文章投诉邮箱:anhduc.ph@yahoo.com