在上一节从头学C(34)外部变量中,我们留了一个未说明的问题,就是为什么说 pop_push.c 中的栈相关信息对 main 函数来说是隐藏的。
第四章 函数与程序结构 >> 4.4 作用域规则
1. 名字的作用域
名字的作用域是指程序中可以使用该名字的部分。
对于在函数开头位置声明的自动变量(局部变量)来说,它的作用域是声明该变量名的函数。所以,不同函数内声明的同名变量之间是没有任何关系的。包括函数的参数,也可以把它看做是局部变量。
对于外部变量或函数,它们的作用域是从声明它的地方开始,到其所在的(待编译的)源文件的末尾结束。
如果在同一个源文件中,按如下顺序依次定义:
main() {......} int sp = 0; double val[MAXVAL]; void push(double f) {......} double pop() {......}
那么 push 和 pop 函数可以直接访问变量 sp 和 val[ ] ,但是 main 既不可以访问这两个变量,也不能访问 push 和 pop。
而在上一节中,因为变量 sp 和 val[ ] 都是在 pop_push.c 中定义的外部变量(它们的作用域也仅限于 pop_push.c ),main.c 没有声明这两个变量,因此 main 函数不能访问这两个变量。
当然,可以通过在 main.c 中使用关键字 extern 来声明这两个变量,则 main 函数便可访问这两个变量了。(虽然我们可以这么做,但如前所说,栈的细节我们在 main 函数中并不关注)
2. 外部变量的声明和定义
声明和定义的概念一定要区分开:
- 变量声明用于说明变量的属性(主要是变量类型);
- 变量定义除了说明变量的属性,还将引起存储器的空间分配。
如果将下列语句放在所有函数外部:
int sp; double val[MAXVAL];
那么这两条语句将定义外部变量 sp 和 val,并为它们分配存储单元,同时作为该源文件中其余部分的变量声明。
而下面这两条语句:
extern int sp; extern double val[];
则为源文件的其余部分声明了一个 int 型变量及一个 double 数组类型的外部变量 val(数组长度在其他地方定义),但这两条语句也仅仅也只是声明而已,并没有为它们建立变量或分配存储空间。
另外还需要注意的地方是:
- 在一个程序的所有源文件中,一个外部变量只能在某个文件中被定义一次,其他文件可以通过 extern 声明访问它们;
- 外部变量的定义中必须指定数据长度,但 extern 声明可以不用指定具体长度;
- 外部变量的初始化只能出现在其定义中;