静态内部变量
上一篇文章,我们用static改变外部变量的链接性质,现在我们继续讨论static,不过,这一次它改变的是内部变量的存储性质(storage)。
一般情况下,内部变量都被安排放在栈里面,函数被调用时才存在,函数结束时就消失。可是,有时我们可能想定义一种变量,编译器在数据段为它分配空间,从而它的生存期是整个程序的运行时间,但这些变量同时又是属于某个函数的内部变量,所以其他函数不能访问到它。很明显,这种变量既有外部变量的存储性质(放在数据段),又具有普通内部变量的可见范围(只能被自身所在的函数访问)。能做到吗?
当然可以,只要使用static。
举个例子:
/*Example C code*/
#include<stdio.h>
int fun()
{
static int a = 0; //定义静态内部变量
return (++a);
}
int main(void)
{
printf(“a = %d\n”, fun());
printf(“a = %d\n”, fun());
return 0;
}
编译、执行,先看看输出的结果:
$gcc test.c
$./a.out
a = 1
a = 2
$
如果没有static关键字,那么两次输出的a显然应该相同,因为每一次进入函数,内部变量a就被初始化为“
也许有人会疑惑:第二次执行函数fun时,a不是应该再次被初始化为“
实际上,静态内部变量a只会被初始化一次,看看汇编代码的片段:
$gcc –S test.c
$cat test.s
.data
.type a.0, @object
.size a.0, 4
a.0:
.long 0
.text
.globl fun
.type fun, @function
fun:
pushl %ebp
movl %esp, %ebp
incl a.0
movl a.0, %eax
popl %ebp
ret
...
注意一下会发现静态内部变量a在名字上被编译器作了一点“小动作”(各种编译器的具体做法不一定相同),成了“a
明显的,a.0被放在数据段,而且由于没有“.globl”语句,因此其他文件也看不见它,否则便可能会因为在不同的文件中定义了相同名称的变量而产生名字冲突。试想一下,有两个C文件:
1.c int f(){ static int a = 0; }
2.c int g(){ static int a = 1; }
如果编译器把这两个变量a都改为“a
.long 0告诉编译器a.0的初始值是“
movl $0, a.0
根本没有。
进入fun函数,马上接着的就是inc(变量自增),事实表明,a.0在程序加载时已经完成了初始化。
由于静态内部变量通过这种方式初始化(不仅静态内部变量,其实所有存放在.data段的数据比如外部变量都是这样初始化的),所以用来初始化静态内部变量的值必须是编译期便可求出的常数,像上面的“
譬如说,我们可以写:
int f();
void go(){ int a = f(); }
但不能写:
int f();
void go(){ static int a = f(); }
这是因为上面用来初始化静态内部变量的值不是常数而是一个函数的返回值,而函数的返回值不可能在编译时就算出来。