这周的大部分时间都花了debug上,,在费了一番周折才debug成功之后发现原来是static局部变量搞得鬼。我意识到原来我对于static的用法理解地还不够透彻,所以想结合这个例子再总结一下。希望在今后的coding中,不在同一个地方跌倒。
下边是错误代码的示例:
Class A{
int foo1(int a);
};
int A::foo1(int a){
static int i = A_ARRAY[a]; // int i = A_ARRAY[a];
// 业务代码若干
return i;
}
const int A_ARRAY[8]={1,2,3,4,5,6,7,8};
在 A::foo1(int a)中,我本意是想通过参数 a 来更新变量 i 的值,但是由于忽视了static在修饰静态局部变量时只能初始化一次,便出现 i 的值在初始化后无法更新的情况。正确的代码应该去掉static修饰符。
但是在软件实际运行过程中,在绝大多数情况下,变量 i 的值在初始化之后便无需更新了。而在某一项极端的测试用例下,出现死机的现象,而在这一用例下发现初始化后的 i 是需要再次更新的。
上边便是我debug的血泪史。其实总结下来,static主要有以下用法:
- 在修饰变量的时候,static 修饰的静态局部变量只执行初始化一次,而且延长了局部变量的生命周期,直到程序运行结束以后才释放。
- static 修饰全局变量的时候,这个全局变量只能在本文件中访问,不能在其它文件中访问,即便是 extern 外部声明也不可以。
- static 修饰一个函数,则这个函数的只能在本文件中调用,不能被其他文件调用。static 修饰的变量存放在全局数据区的静态变量区,包括全局静态变量和局部静态变量,都在全局数据区分配内存。初始化的时候自动初始化为 0。
- 不想被释放的时候,可以使用static修饰。比如修饰函数中存放在栈空间的数组。如果不想让这个数组在函数调用结束释放可以使用 static 修饰。
- 考虑到数据安全性(当程序想要使用全局变量的时候应该先考虑使用 static)。
脑海中的理解并不算真正的理解,只有实现出来才是真的。
Talking is cheap Show me the code
假如我们有A.cpp, A.h。
A.cpp
#include "A.h" static int b = 666; //静态全局变量 static int foo1(void); //静态全局函数 int foo1(void){ b++; return 0; }
A.h
#ifndef A_H_
#define A_H_
extern int b;
#endif
针对上边提到的 static 的5个用法,我逐一举例说明。
第一条便是我的例子。
第二条可以拿 A.cpp 中的 static int b 举例。假如 B.cpp 引用了 A.h, b 虽然在 A.h 中有 b 的声明,但在B.cpp也是无法使用 b。因为 b 是全局静态变量,作用域只在 A.cpp 中。
#include "A.h"
int foo2(void){
b++;
return 0;
}
第三条 A.cpp 中的 foo1(void) 只能在 A.cpp 中使用,而不能在 B.cpp中引用。
第四条 由于static变量存储在静态存储区,所以在函数调用完成后不会释放变量。而如果没有static修饰符,在函数重新被调用的时候变量会被再次初始化。下边是一个简单的例子:
#include <iostream>
using namespace std;
int foo1(void){
static int i = 10;
return i++;
}
int foo2(void){
int j = 10;
return j++;
}
int main(void){
cout << "第一次 foo1()=" << foo1() <<endl;
cout << "第一次 foo2()=" << foo2() <<endl;
cout << "第二次 foo1()=" << foo1() <<endl;
cout << "第二次 foo2()=" << foo2() <<endl;
return 0;
}
输出结果:
第一次 foo1()=10
第一次 foo2()=10
第二次 foo1()=11
第二次 foo2()=10
第五条 static能够保证全局变量的作用域只在当前的文件中,能够提高数据的安全性。比如 A.cpp 中的变量 b 是受到保护的,不会因为在A.h或者其他头文件中的声明而改变作用域。