void swip() /*定义 swip 函数,功能是交换变量 a 和 b 的值*/
{int t;
t=a;
a=b;
b=t;
} 运行:
Input
********
a=10000↓
b=30000↓
********
Output
********
a=30000 b=10000
********
全局变量增加了函数间数据联系的渠道,然而,全局变量的使用使得函数的通用性降低,
因为函数在执行时要依赖于其所在的外部变量。过多使用全局变量,会降低程序的清晰性。另 外,全局变量在程序的整个执行过程中都占用存储单元。
如果在同一个源程序文件中,外部变量与局部变量同名,则在局部变量的作用范围内,
外部变量被“屏蔽”,因而不受干扰。
8.7 变量的存储类别
8.7.1 动态存储方式与静态存储方式
从变量的作用域(即从空间)来分,可以把变量分为全局变量与局部变量。从变量值存 在的时间(即生存期)来分,可以把变量分为静态存储方式和动态存储方式。静态存储方式是 指在程序运行期间分配固定的存储空间的方式。动态存储方式是指在程序运行期间根据需要动 态地分配存储空间的方式。
内存中提供用户使用的存储空间示意图如图 8.7 所示,这个存储空间分为 3 个部分:
(1)程序区。
(2)静态存储区。
(3)动态存储区。
内存用户区 程序区 静态存储区 动态存储区
图 8.7 内存中提供用户使用的存储空间示意图
数据分别存放在静态存储区和动态存储区中。全局变量存放在静态存储区中,在程序开 始执行时给全局变量分配存储区,程序执行完毕就释放这些空间。在程序执行过程中它们占用
固定的存储单元,而不是动态地进行分配和释放。
在动态存储区中存放以下数据:
(1)函数的形式参数。
(2)自动变量(未加 static 声明的局部变量)。
(3)函数调用时保护现场和返回地址等。
在 C 语言中数据的存储类别分为两大类:静态存储类和动态存储类。具体包含 4 种:自 动的(auto)、静态的(static)、寄存器的(register)和外部的(extern)。根据变量的存储类别,
可以确定变量的作用域和生存期。
8.7.2 auto 变量
用 auto 说明类别的变量或者在函数中未说明存储类别的局部变量称为自动变量。函数中 的形式参数和在函数中定义的,包括在复合语句中定义的不加以类别说明的变量,都属于自动 变量。自动变量是自动地分配存储空间的,数据存储在动态存储区中,在调用该函数时系统予 以分配空间,在函数结束时就自动释放所占用的空间。
8.7.3 用 static 声明的局部变量
使用 static 说明其类别的局部变量称为静态局部变量。
静态局部变量在函数调用结束后,变量的值不消失,即不释放所占用的存储单元,在下 一次调用函数时,静态局部变量保存着原来的值。在 C 语言中静态局部变量只被初始化一次。
可以对静态局部变量与自动局部变量作一个比较:静态局部变量属于静态存储类别,在 静态区内分配存储单元,在程序整个运行期间都不释放所占用的空间;而自动变量属于动态存 储类别,在动态区内分配存储单元,在函数调用结束后释放所占用的空间。静态局部变量只在 编译时初始化一次,以后每次调用函数时不再初始化变量,而且函数调用结束后不释放所占用 的存储空间,从而保留了上一次函数调用结束时的值;而自动变量不是在编译时赋初值,而是 在调用函数时初始化变量,每调用一次函数就初始化一次,而且函数调用结束后,就释放了所 占用的存储空间。当静态局部变量未初始化时,编译系统自动对数值型变量赋初值 0,对字符 变量赋空值;而自动变量不初始化,则它的值是原来单元中残留的一个随机值。这是由于每次 函数调用结束后存储单元已经释放,下次调用时又重新另外分配存储单元所造成的。
当需要保留函数上一次调用的值时,或是当初始化后,变量只被引用而不改变其值的时 候使用静态局部变量较好。
例 8.16 分析下列 2 个程序,写出程序的运行结果。
程序 1:
/*静态变量 x*/
#include "stdio.h"
main() {int i;
void f();
for (i=1;i<=6;i++) f();
printf("\n");
}
void f()
{static int x=0;
x=x+1;
printf("x=%-4d",x);
} 运行:
x=1 x=2 x=3 x=4 x=5 x=6 程序 2:
/*自动变量 x*/
#include "stdio.h"
main() {int i;
void f();
for (i=1;i<=6;i++) f();
printf("\n");
}
void f() {int x=0;
x=x+1;
printf("x=%-4d",x);
} 运行:
x=1 x=1 x=1 x=1 x=1 x=1 例 8.17 打印 1~5 的阶乘值。
程序:
int fac(int n) {static int f=1;
f=f*n;
return(f);
}
#include "stdio.h"
main() {int i;
for (i=1;i<=5;i++)
printf("%d!=%d\n",i,fac(i));
} 运行:
1!=1 2!=2 3!=6 4!=24 5!=120
8.7.4 register 变量
为了提高程序执行效率,C 语言允许将局部变量的值存放在 CPU 的寄存器中,需要使用 时直接从寄存器中取出数据参加运算,不必到内存中去存取。由于寄存器的存取速度远远高于 内存的存取速度,因此可以提高程序的执行效率。
例 8.18 使用寄存器变量,分别求 1!、2!、3!、4!和 5!的值。
程序:
/*使用寄存器变量,打印 1~5 的阶乘值*/
int fac(int n)
{register int i,f=1; /*定义寄存器变量*/
for (i=1;i<=n;i++) f=f*i;
return(f);
}
#include "stdio.h"
main() {int i;
for (i=1;i<=5;i++)
printf("%d!=%d\n",i,fac(i));
} 运行:
1!=1 2!=2 3!=6 4!=24 5!=120
只有局部自动变量和形式参数可以作为寄存器变量。在调用一个函数时占用一些寄存器 以存放寄存器变量的值,在函数调用结束时释放寄存器。一个计算机系统中的寄存器数目极为 有限,不能过多定义寄存器变量。当今的优化编译系统能够识别使用频繁的变量,从而自动地 将这些变量放在寄存器中,而不需要程序设计者指定。局部静态变量不能定义为寄存器变量。
8.7.5 用 extern 声明的外部变量
外部变量即全局变量是在函数外部定义的,它的作用域为从变量的定义处开始,到本程 序文件的末尾。在此作用域内,全局变量可以为程序中各个函数所引用。编译时将外部变量分 配在静态存储区。有时需要用 extern 来声明外部变量,以扩展外部变量的作用域。
1.在一个文件内声明外部变量
如果外部变量不在文件的开头定义,其有效的作用范围只限于定义处到文件结束。如 果在定义点之前的函数想引用该外部变量,则应在引用之前用 extern 对该变量做外部变量 声明。
例 8.19 用 extern 声明外部变量,扩展它在程序文件中的作用域。
程序:
/*用 extern 声明外部变量,扩展它在程序文件中的作用域。*/
#include "stdio.h"
main() {int a,b,c;
int max(int x,int y); /*声明有参函数 max*/
extern int A,B; /*外部变量声明*/
c=max(A,B);
printf("max=%d\n",c);
}
int A=12,B=-8; /*定义外部变量*/
int max(int x,int y) /*定义有参函数 max*/
{int z; /*函数中的说明部分*/
if (x>y) z=x;
else z=y;
return(z);
} 运行:
max=12
2.在多个文件程序中声明外部变量
如果一个程序包含两个以上的文件,在两个文件中都要用到同一个外部变量 x,那么,不 能分别在两个文件中各自定义一个外部变量 x,否则在进行程序的链接时会出现“重复定义”
的错误。在出现这种情况时,应在其中一个文件中定义外部变量 x,在另一个文件中用 extern 对 x 变量做外部变量声明。声明变量 x 的语句如下所示:
extern x;
例 8.20 将变量 a 和 b 定义为全局变量,设计程序,交换变量 a 和 b 的值。
文件 file1.c 内容如下所示:
/*文件名:file1.c*/
/*用 extern 将外部变量的作用域扩展到其他文件。*/
#include "stdio.h"
int a,b; /*定义外部变量*/
main()
{void swip(); /*函数原型*/
printf("a=");
scanf("%d",&a);
printf("b=");
scanf("%d",&b);
swip();
printf("a=%d\nb=%d\n",a,b);
}
文件 file2.c 内容如下所示:
/*文件名:file2.c*/
/*用 extern 将外部变量的作用域扩展到其他文件。*/
extern a,b; /*声明 a 和 b 为已定义的全局变量*/
void swip() /*定义 swip 函数,功能是交换变量 a 和 b 的值*/
{int t;
t=a;
a=b;
b=t;
}
当编译链接以上程序后便可以运行程序,运行结果如下所示:
a=8↓
b=5↓
a=5 b=8
在以上程序中,使用了全局变量 a 和 b,因此这两个变量的作用范围是整个程序,但由于 这个程序是由 2 个源程序文件组成的,因此,在使用全局变量 a 和 b 时,应在一个源程序文件 中定义,在另一个源程序文件中声明。以上的程序是在 file1.c 中定义变量 a 和 b 的,而在 file2.c 中对它们进行声明的。在下一节中将介绍如何将多个源程序文件构成的一个程序编辑链接成一 个可执行文件的方法。