从变量的作用域角度来看,变量可以分为局部变量(自动变量)和全局变量(外部变量);从变量的生存期角度来看,变量可以分为静态存储变量和动态存储变量。
今天主要介绍的是静态存储变量,也就是用关键字static声明了的变量。所谓静态存储就是指在程序运行期间由系统分配固定的存储空间的方式(程序开始执行时分配,在程序完毕时释放,在程序过程中它们占据国定的存储单元,而不是动态分配和释放)。
首先要说一下变量在内存中的存储。其实在内存中,不同存储类型的变量在内存中存放的区域是不同的。具体来说,在C语言中,内存主要分为如下5个存储区:
(1)栈(Stack):位于函数内的局部变量(包括函数实参),由编译器负责分配释放,函数结束,栈变量失效。
(2)堆(Heap):存储动态申请的数据,由程序员用malloc/calloc/realloc分配,free释放。如果程序员忘记free了,则会造成内存泄露,程序结束时该片内存会由OS回收,但程序只要不结束,就有可能造成内存泄露。
(3)全局区/静态区(Global Static Area):全局变量和静态变量存放区,程序一经编译好,该区域便存在。由于全局变量一直占据内存空间且不易维护,推荐少用。程序结束时释放。
(4)C风格字符串常量存储区: 专门存放字符串常量的地方,程序结束时释放。
(5)程序代码区:存放程序二进制代码的区域。
可以看到,用static声明了的变量就应该存储在全局区/静态区的,因为这个区域是存放全局变量和静态变量的,所以全局变量和静态变量是有一些共同点的:
1)在没有显式初始化的情况下,外部变量和静态变量都将被初始化为0;
2)外部变量和静态变量的初始化表达式必须是常量表达式。
接下来说说static的作用,主要有2个:
1)隐藏(对于全局变量而言)
在含有多个文件的程序中,所有未加static前缀的全局变量都是具有全局可见性的,也就是说除了变量所在的文件外,其它文件中的函数都可以访问这个变量。如果加上static后,那么这个全局变量就只能被本文件中的函数访问了。举例说明:
// 在 file1.c 中
static int i = 1;
// 在file2.c 中
#include <stdio.h>
int main()
{
extern int i; //声明i是一个全局变量
printf("%d\n", i);
return 0;
}
这两个文件单独编译时都没有问题,但是链接时就提示错误了:unresolved external symbol _i,无法确定的外部符号 i,因为链接时找不到file2.c中声明的i(被file1.c中的static隐藏了),所以就会报错了。
当static用来修饰全局变量的时候,它就改变了全局变量的作用域(在声明他的文件之外是不可见的),但是没有改变它的存放位置,还是在静态存储区中。
2)保持功能持久性(对于局部变量而言)
在局部变量之前加上关键字static,局部变量就被定义成为一个局部静态变量。其作用域没变还是局部作用域,但是它改变了局部变量的存储位置,从原来的栈中存放改为静态存储区。局部静态变量在离开作用域之后,并没有被销毁,而是仍然驻留在内存当中,直到程序结束,只不过我们不能再对他进行访问。举例说明:
#include <stdio.h>
void fun()
{
static int i = 0;
printf("%d ", i++);
}
int main()
{
int n;
for(n = 0; n != 10; n++)
fun();
printf("\n");
return 0;
}
运行结果是0 1 2 3 4 5 6 7 8 9
可以看到每次fun函数运行完后,i的值还是保留着的,等下次运行fun函数时,是在上一次i的值的基础上执行的。静态局部变量只在第一次进入函数时定义初始化,后面就不再重新初始化了。