static修饰变量
- 如果 静态变量在函数外声明:
static int x = 10;
int fun() {}
int main() {}
这种变量在内存的 .data区,但作用域和可见性为本文件,即链接性为内部的,其他文件不可访问。
- 如果静态变量在函数内:
int fun(int a)
{
static int x = a;
x = a;
return x;
}
对于这种情况,静态变量x 的作用域为fun()函数,但它仍然存储在.data区,生存期延长至程序结束。函数内第一条语句,是对x的初始化,不管函数执行多少次,初始化只有一次。
- 如果在一个文件中定义了一个全局变量,其具有外部链接性,在第二个文件中定义了一个同名的静态变量:
//file 1
int num = 10;
___________________________
//file 2
static int num = 20;
void fun()
{
cout << num << endl;
}
此时,静态变量隐藏常规的外部变量,所以会打印 静态的变量。
用字面常量和变量初始化静态变量的区别(重点)
- 以字面常量初始化静态变量
void funa()
{
static int x = 10;
int* ip = &x;
}
int main(void)
{
funa();
return 0;
}
通过反汇编查看,并没有发现 static int x = 10这条语句的执行
对于这种情况,编译器在编译时,会发现funa()函数中有一个拿字面常量初始化的静态变量,那么编译器会直接把这条语句删掉,把静态变量 x 记录起来并初始化为 10;在程序执行到进入主函数时,会为这个静态变量 x 在数据区创建空间。
图示
- 以变量初始化静态变量
void funb(int a)
{
static int x = a;
}
int main(void)
{
funb(10);
funb(20);
return 0;
}
这种模式的初始化流程为:
- 编译器会直接给静态变量 x 在数据区开辟空间,这个空间会有一个标记位置,标记位初始化为0。
- 在进入主函数后,调用 funb(10); 这时候会用10来给 静态变量x 初始化,初始化完成后,标记位变为1。
- 再调用 funb(20)的时候,会发现 x的标记位已经变成 1 ,则就不能再进行初始化。
反汇编与内存查看:
在执行到静态变量这条语句时,可以发现反汇编有大量的语句,其中就有检查标记位
再次执行第二个funb()函数时,会检查标记位为 1后,就会跳过赋值步骤。
图示:
第二种相对于第一种情况,在多线程模式下并不安全,因为 两个函数funb()会同时竞争 静态变量x和标记位 flag 的资源,导致 x 不止被初始化一次。
static修饰函数
如果将函数定义为静态,和全局的静态变量一样,其可见性为本文件有效,同一个工程的其他文件不可用。
错误示例:
//file1
static int fun()
{
//...
}
________________________
//file 2
int test()
{
int x = fun(); //error
}
static在类中的使用
类的静态变量
设计一个静态变量作为类成员。
示例代码
class Object
{
private:
int value;
static int num;
public:
Object(int x = 0) : value(x)
{
num += 1;
}
~Object()
{
num -= 1;
}
Object(const Object& obj) : value(obj.value)
{
num += 1;
}
};
int Object::num = 0;
‘注意点:
- 类的静态变量必须在类外初始化
- 类的静态变量,所有该类的对象共用一份
示例:
int main(void)
{
Object obja(10);
Object objb(20);
return 0;
}
根据监视器可以看出:
- 两个对象的普通变量都是自己的,而静态变量num共用一份。
- 在对象中并没有看到静态变量属于谁,这就意味着,无法通过this指针来约束静态变量。
示例:在刚才类的基础上加上一个常方法
void Print() const
{
num += 10;
cout << "value = " << this->value << "num = " << this->num << endl;
}
int main(void)
{
Object obja(10);
obja.Print();
Object objb(20);
objb.Print();
return 0;
}
这样是可以编译通过的,可以看出对象模型里没有 num,所以即使使用const限制this指针,还是可以对 静态变量修改。
类的静态函数
示例:如果把非静态函数Print()的内容写成静态函数,那么还能编译通过吗?
static void Show()
{
num += 10;
cout << this->value << endl;
cout << num << endl;
}
注意:
静态函数和非静态函数最大的区别就是:
非静态函数有this指针,而静态函数没有this指针,所以若要输出 value,编译器会不知道输出谁的value,编译出错。