分享

C /C入门之static是个雷

 走出尘埃 2016-06-23

找不到出错原因,让C++码农深感受挫;然而让他们觉得生无可恋的,还是同一段代码,时而出错,时而运行良好。-- 题记

C++/C入门之static是个雷

对于初学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=""><>

}

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=""><>

}

};

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的初始值,取决于被加载的顺序,而这个顺序,是不能被保证的!

有不少有效的办法,来避免这个情况的出现。最有效的办法还是,避免静态对象依赖的出现。

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多