2.4 静态变量
2.4.1 什么是静态变量
静态变量是从程序启动到运行结束为止持续存在的变量。因此,静态变量总是在虚拟地址空间上占有固定的区域。
静态变量中有全局变量、文件内 static
变量和指定 static
的局部变量。因为这些变量的有效作用域各不相同,所以编译和连接时具有不同的意义,但是运行的时候它们都是以相似的方式被使用的。
2.4.2 分割编译和连接
在 C 语言中,一个程序可以由多个源代码文件构成,并且这些源代码文件在各自编译之后可以连接起来。这一点对于大规模的编程工作来说,是举足轻重的。难道不是吗?如果 100 个程序员一拥而上,一起捣鼓同一个源代码文件,那真是不可想象。
此外,关于函数和全局变量,如果它们的名称相同,即使它们跨了多个源代码文件也被作为相同的对象来对待。进行这项工作的是一个被称为“链接器”的程序。
图 2-4 链接器
为了在链接器中将名称结合起来,各目标代码大多都具备一个符号表(symbol table)(详细内容需要依赖实现细节)。比如在 UNIX 中,可以使用 nm
这样的命令窥视符号表的内容。
不好意思,这里是 UNIX 特有的话题。为了测试,我们使用 cc –c
编译 print_address.c(参照代码清单 2-2),生成 print_addres.o 文件,之后对这个文件使用 nm
命令。在我的环境中,结果输出如下。
> cc -c print_address.c
> nm print_address.o
00000004 b file_static_variable
00000000 T func1
00000000 b func1_static_variable.4
0000002c T func2
00000000 t gcc2_compiled.
00000004 C global_variable
00000048 T main
U malloc
U printf
通过观察输出结果,我们很容易发现符号表中记录了文件内的 static
变量、局部 static
变量这些看上去不需要连接的对象。
如果命名空间不一样,确实不需要和其他文件的符号连接,但是对于静态变量来说,因为必须要给它们分配一些地址,所以符号表中记录了这些变量。可是同时我们也发现全局变量的标记有些特别。与全局变量使用 C 进行标记不同的是:和外部没有连接的符号,无论是局部的还是文件内部的 static
变量,都使用了 b
进行标记。
局部 static
变量 func1_static_variable
的后面被追加了.4
这样的标记,这是因为在同一个.o
文件中,局部 static
变量的名称有可能会发生重复,所以在它后面追加了识别标记。
函数名后面追加了 T
或者 U
。如果函数是在当前文件中定义的,就在其函数名后加 T
;如果函数定义在当前文件之外,只是在当前文件内部调用此函数,就在此函数后面加 U
。
gcc2_compiled.
是我们没有见过的符号,你可以把它当成处理环境随意加上的标记,把它放在一边。
链接器就是根据这些信息,给这些到目前为止还只是个“名称”的对象分配地址*。
* 现在对共享库做动态链接变成一件很自然的事,而现实中可没有这么单纯……
请注意自动变量完全没有出现在符号表中。这是因为自动变量的地址是在运行时被决定的,它属于链接器管辖范围以外的对象。关于这一点,我们在下一节阐述。