要区分声明周期和作用域。
static 局部变量,会存在data段,是其在整个程序执行过程中都存在,并且只有一份。
data段是一个全局的内存空间,且变量以保持一份。
类的成员变量会在程序运行前初始化,在程序退出后析构。
函数局部静态变量,在第一次使用时初始化,在程序退出后释放。
使用字面常量初始化一个本地静态 POD 数据(局部静态变量),实际上,这类初始化并不是在程序第一次执行到该变量所在语句块时才进行的,而是在程序启动时就直接从映像文件内的数据段中加载。
全局变量在进程启动的时候,在同一个编译单元内定义的全局量会按照其定义顺序被依次地初始化。
对于 POD 类型,一种更好的解决方法是:充分利用操作系统加载进程时,对数据段做全零初始化的特性:C++ 标准中明确规定了,所有静态成员(即进程数据段)在进程加载时都必须 "zero-initialized"
1.什么是static?
static 是C/C++中很常用的修饰符,它被用来控制变量的存储方式和可见性。
1.1static的引入
我们知道在函数内部定义的变量,当程序执行到它的定义处时,编译器为它在栈上分配空间,函数在栈上分配的空间在此函数执行结束时会释放掉,这样就产生了一个问题: 如果想将函数中此变量的值保存至下一次调用时,如何实现? 最容易想到的方法是定义为全局的变量,但定义一个全局变量有许多缺点,最明显的缺点是破坏了此变量的访问范围(使得在此函数中定义的变量,不只受此函数控制)。static关键字则可以很好的解决这个问题。
另外,在C++中,需要一个数据对象为整个类而非某个对象服务,同时又力求不破坏类的封装性,即要求此成员隐藏在类的内部,对外不可见时,可将其定义为静态数据。
1.2静态数据的存储
全局(静态)存储区:分为DATA段和BSS段。DATA段(全局初始化区)存放初始化的全局变量和静态变量;BSS段(全局未初始化区)存放未初始化的全局变量和静态变量。程序运行结束时自动释放。其中BBS段在程序执行之前会被系统自动清0,所以未初始化的全局变量和静态变量在程序执行之前已经为0。存储在静态数据区的变量会在程序刚开始运行时就完成初始化,也是唯一的一次初始化。
在C++中static的内部实现机制:静态数据成员要在程序一开始运行时就必须存在。因为函数在程序运行中被调用,所以静态数据成员不能在任何函数内分配空间和初始化。
这样,它的空间分配有三个可能的地方,一是作为类的外部接口的头文件,那里有类声明;二是类定义的内部实现,那里有类的成员函数定义;三是应用程序的main()函数前的全局数据声明和定义处。
静态数据成员要实际地分配空间,故不能在类的声明中定义(只能声明数据成员)。类声明只声明一个类的“尺寸和规格”,并不进行实际的内存分配,所以在类声明中写成定义是错误的。它也不能在头文件中类声明的外部定义,因为那会造成在多个使用该类的源文件中,对其重复定义。
static被引入以告知编译器,将变量存储在程序的静态存储区而非栈上空间,静态数据成员按定义出现的先后顺序依次初始化,注意静态成员嵌套时,要保证所嵌套的成员已经初始化了。消除时的顺序是初始化的反顺序。
优势:可以节省内存,因为它是所有对象所公有的,因此,对多个对象来说,静态数据成员只存储一处,供所有对象共用。静态数据成员的值对每个对象都是一样,但它的值是可以更新的。只要对静态数据成员的值更新一次,保证所有对象存取更新后的相同的值,这样可以提高时间效率。
2.在C/C++中static的作用
2.1总的来说:
(1)生命周期:在修饰变量的时候,static修饰的静态局部变量只执行初始化一次,而且延长了局部变量的生命周期,直到程序运行结束以后才释放,但不改变作用域。比如修饰函数中存放在栈空间的数组。如果不想让这个数组在函数调用结束释放可以使用static修饰。
(2)可见性:static修饰全局变量或函数时,这个全局变量只能在本文件中访问,不能在其它文件中访问,即便是extern外部声明也不可以。这个函数也只能在本文件中调用,不能被其他文件调用。
(3)存储方式:Static修饰的变量存放在全局数据区的静态变量区,包括全局静态变量和局部静态变量,都在全局数据区分配内存。初始化的时候自动初始化为0。
(4)考虑到数据安全性(当程序想要使用全局变量的时候应该先考虑使用static)。
2.2静态变量与普通变量
静态全局变量有以下特点:
(1)静态变量都在全局数据区分配内存,包括后面将要提到的静态局部变量;
(2)未经初始化的静态全局变量会被程序自动初始化为0(在函数体内声明的自动变量的值是随机的,除非它被显式初始化,而在函数体外被声明的自动变量也会被初始化为0);
(3)静态全局变量在声明它的整个文件都是可见的,而在文件之外是不可见的。
优点:静态全局变量不能被其它文件所用;其它文件中可以定义相同名字的变量,不会发生冲突。
(4)全局变量和全局静态变量的区别
1)全局变量是不显式用static修饰的全局变量,全局变量默认是有外部链接性的,作用域是整个工程,在一个文件内定义的全局变量,在另一个文件中,通过extern 全局变量名的声明,就可以使用全局变量。
2)全局静态变量是显式用static修饰的全局变量,作用域是声明此变量所在的文件,其他的文件即使用extern声明也不能使用。
2.3静态局部变量有以下特点:
(1)该变量在全局数据区分配内存;
(2)静态局部变量在程序执行到该对象的声明处时被首次初始化,即以后的函数调用不再进行初始化;
(3)静态局部变量一般在声明处初始化,如果没有显式初始化,会被程序自动初始化为0;
(4)它始终驻留在全局数据区,直到程序运行结束。但其作用域为局部作用域,当定义它的函数或语句块结束时,其作用域随之结束。
一般程序把新产生的动态数据存放在堆区,函数内部的自动变量存放在栈区。自动变量一般会随着函数的退出而释放空间,静态数据(即使是函数内部的静态局部变量)也存放在全局数据区。全局数据区的数据并不会因为函数的退出而释放空间。
C++ 标准保证局部静态变量会在第一次使用时被初始化,并按照与初始化相反的顺序在进程结束时销毁。但是,C++ 不保证局部静态变量初始化的多线程安全性。实际上,大多数编译器是使用类似如下的技巧实现本地静态变量初始化的:
用户代码: void func(void) { static int s_nMyVar = MyCalcAlgorithm(); // ... } 编译器生成的代码: void func(void) { static int bCompilerInitFlag; // 初始化标记 static int s_nMyVar; if (FALSE == bCompilerInitFlag) { bCompilerInitFlag = TRUE; s_nMyVar = MyCalcAlgorithm(); } // ... } |
static关键字
C语言中的static:
1. 生命周期:static修饰的变量,存储在内存的静态存储区,无论是在函数内还是函数外,生命周期都是整个程序运行期间,static修饰的函数其生命周期也是整个程序的运行期间
2. 作用域:static修饰的局部变量的作用域就是定义该变量的那个作用域内,static修饰的全局变量的作用域是从这个变量定义开始到整个程序结束
3. static修饰的变量都存储在静态区
4. static修饰函数或者全局变量会改变他们的链接属性,只具有内部链接属性。也就是说,被static修饰的全局变量或者函数在其他文件中不能使用,只能在源文件中使用
5. static修饰的变量默认初始化为0
C++中的static:
1. static修饰的类成员变量为静态成员变量
1.静态成员变量一定要在类外进行初始化
2. 静态成员变量为所有的类对象所共享
3. 静态成员变量必须在类中声明,类外定义,定义时不用加static关键字
4. 包含静态成员变量的类对象的大小并不包含静态成员变量的大小
2. static修饰类成员函数为静态成员函数
1. 静态成员函数必须在类中声明,类外定义,定义时不用加static关键字
2. 静态成员函数为所有类对象所共享
3. 静态成员函数也有访问权限
4. 静态成员函数内部职能访问静态的成员变量;普通的成员函数可以访问静态成员变量,也可以访问非静态成员变量
5.静态成员函数没有隐藏的this指针不能访问任何非静态成员
编译器是通过类的作用域的方式来访问静态成员的。
静态成员变量和非静态成员变量的区别:
* 静态成员变量必须要放在类外定义
* 静态成员所有类对象共享,普通成员变量每个类对象各自拥有自己的
* 计算类对象的时候不包含静态成员变量
* 普通类型的变量只能借助对象来访问,静态成员变量既可以通过对象来访问也可以通过类的作用域来访问
* 存储的位置不同,普通变量存储在对象所在位置的栈上,静态成员变量存储在数据段
* 它在类对象生成前就已经构造完成,可以对其进行修改
* 如果类声明放在.h文件中,那么static成员的定义必须在.cpp中
静态成员函数和非静态成员函数的区别:
* 静态成员函数中不能访问普通变量—>(普通的成员变量和普通的成员函数)
* 静态成员函数没有this指针,普通成员函数有隐藏的this指针
* 静态成员函数中可以访问静态成员变量(因为类里面所有对象共享)
* 静态成员函数不能被const修饰,因为const修饰的实际上是this指针
* 静态成员函数不能调用非静态成员函数,非静态成员函数可以调用静态成员函数