从字面上看,static的意思是静态的。由static'修饰的变量或函数,改变了其存储方式和可见性,static关键字的作用主要有拓展生命周期、限制作用域和保持数据唯一性。
1、拓展生命周期
拓展生命周期是针对c语言中局部变量,因为static关键字改变了局部变量的存储方式。c/c++中有5种存储区:
(1)栈区
用于存放局部变量、函数的参数、函数的返回地址等,静态分配,由系统自动分配和回收。该数据结构由操纵系统维护。
(2)堆区
存放程序员由new申请分配的空间,动态分配,需要程序员手动释放,如果没有手动释放,将在程序结束后由OS自动回收。由该数据结构由链表实现,库函数支持。
(3)静态区
用于存放全局变量和static变量,在执行到main函数之前由系统自动分配,到程序结束时自动收回。
(4)常量区
用于存放常量,不允许被修改。
(5)代码区
用于存放二进制代码。
将局部变量修饰为static后,该局部变量在静态区分配内存空间,使得该变量的作用域拓展到程序的整个生命周期。
引用一段别人的代码:
int a = 0; //全局初始化区 char *p1; //全局未初始化区 void main() { int b; //栈 char s[] = "abc"; //栈 char *p2; //栈 char *p3 = "123456"; //123456 在常量区,p3在栈上。 static int c = 0; //全局(静态)初始化区 p1 = (char *)malloc(10); p2 = (char *)malloc(20); //分配得来得10和20字节的区域就在堆区。 strcpy(p1, "123456"); //123456 放在常量区,编译器可能会将它与p3所指向的"123456"优化成一块。 }
2、限制作用域
限定作用域是针对全局变量和全局函数,但是没有改变变量的存储方式。该作用与程序的编译有关,每一个cpp文件都是一个独立的编译单元,编译器在把cpp文件编译
为.o文件时,除了在目标文件里面写入代码和数据外,至少还要提供三个表:
(1)未解决符号表
未解决符号表提供了所有在该编译单元中引用但在该编译单元中没定义的符号及其地址。
(2)导出符号表
导出符号表提供了所有在该编译单元中定义并且愿意提供给其他编译单元使用的符号及其地址。
(3)地址重定向表
地址重定向表提供了本编译单元所有对自身地址的引用的记录。
链接器进行链接的时候,首先决定各个目标文件在最终可执行文件里的位置。然后访问所有目标文件的地址重定向表,对其中记录的地址进行重定向(即加上该编译单元实
际在可执行文件里的起始地址)。然后遍历所有目标文件的未解决符号表,并且在所有的导出符号表里查找匹配的符号,并在未解决符号表中所记录的位置上填写实际的地址(也
要加上拥有该符号定义的编译单元实际在可执行文件里的起始地址)。最后把所有的目标文件的内容写在各自的位置上,再作一些别的工作,一个可执行文件就出炉了。
全局变量和全局函数名编译后为默认添加到导出符号表中,但是由static修饰后,告诉编译不导出全局变量或函数的符号,因此除了自身文件,在其他问价中不可访问的,因此
限定了其作用域。如果用extern修饰了,这就告诉编译器这个符号在别的编译单元里定义了,即将该符号添加到未解决符号表中。
3、保持数据唯一性
保持数据的唯一性是针对c++中的成员变量和成员函数,说明该变量是属于整个类的,而不是属于某个对象的。
(1)静态成员变量的定义时就要分配内存空间,因此不能在类中定义;
(2)因为静态成员函数不属于某个对象,因为该函数的调用时没有this指针,也不能访问非static成员变量和函数。