未回传不能取款 5.4 全局变量与局部变量
5.4 全局变量与局部变量
上一节我们学习了函数的调用并分析了函数的执行过程,当一个函数调用另一个函数时,即建立了函数间的信息传递的关系。主调用函数通过实际参数把数据传给被调用函数,被调用函数用传入的数据进行运算后,最后通过语句把结果回传给主调用函数。本节我们讲解变量生命周期、全局变量和局部变量。
变量的生命周期指的是变量从“生”到“死”的时间段。前面定义的函数中的所有变量都是在函数执行时“生”,在函数结束时“死”,且只有定义它的函数才能使用,称为局部变量。局部变量只在函数体内有效,离开了定义它的函数体,其它任何函数都不可见、不可用。所以,不同函数中可以有同名变量,他们是不同的变量,不会互相干扰。如下图,用函数输出2至1000所有素数的实例中,函数与main都定义了变量i,但这两个变量名字相同,但是占用不同的存储单元,都只能被定义它的函数使用。main函数见不到也不能访问中的变量i,反过来,也见不到也不能访问main中的变量i。
int isprime(int n)
{
int i;
...
}
int main( )
{
int i;
for(i=2;i<1000;i++)
if( isprime(i) ) printf("%d ",i);
}
也就是说在函数中可以任意定义自己的变量名,不用担心与其它函数的变量同名。这是程序设计的一种重要的思想——信息隐藏。即自己内部的信息只有自己可见,其它函数甚至主调用函数都看不到被调用函数内部定义的变量。除了函数内定义的局部变量外,还有一种在复合语句或分程序中定义的局部变量,这种变量的生命期更短,只在复合语句中有效,复合语句之外的程序不能使用复合语句内定义的变量。如以下程序:
int main( )
{
int a;
a=10;
{
int a;
a=20;
printf("a1=%dn",a);
}
printf("a2=%dn",a);
return 0;
}
程序在main中定义了一个局部变量a,在复合语句中又定义了一个只作用在复合语句的同名局部变量a。在这种情况下,复合语句内的变量a与外面的变量a是不同的变量,因此复合语句输出的是它内部的变量a的值,而main中的输出语句输出的是main中的变量a的值,程序执行后结果是:
a1=20
a2=10
但是在某些时候,多个函数需要共享一些变量,这就需要打破信息隐藏的规则。如:银行余额的查询、存取款等就需要定义一个被多个函数都能访问的变量,这种能被所有函数共享的变量称为全局变量。全局变量不属于某个函数,因此只能在函数之外定义,全局变量的生命周期从定义开始到整个程序结束都有效。我们看一下银行存款模拟的实例。
例1:个人银行业务模拟。假定某人到银行存钱之前要先在银行开户,并至少存入10元钱,开户后,此人在银行的存款余额为刚存入的金额。有了帐户后,此人可查询自己的存款余额、存入一定金额、取出不多于存款余额的金额等。试编写一个程序模拟个人银行业务。
首先要解决银行存款余额的问题。可以考虑用一个双精度实型变量来表示存款余额,由于可以被查询、存款(增加)、取款(减少)。因此,变量只能定义为全局变量,这样就可以被查询、存款和取款等操作访问。如果的初值为-1,表示未建立帐户,对于未创建的帐户不能查询、存取款。如:
double balance=-1;
由于个人银行业务可以有建立帐户、查询、取款、存款等四类操作。因此可以分别用4个函数、query、,等表示。
函数的作用只是存入超过10元的金额x,所以函数形式参数为x,并且没有返回值,函数代码为:
void createAcc(double x)
{
if(x<10)
printf("Can't create account!n");
else
balance=x;
}
查询帐户余额函数query只是返回全局变量的余额,因此函数没有形式参数,但是有返回值。要注意函数需要判断该帐户是否已创建,可以通过是否为-1来判断,函数为:
double query( )
{
if(balance==-1){
printf("Account is not exists!n");
exit(0);
}
else
return balance;
}
函数中调用了exit函数,该函数表示直接结束当前运行的程序,其头文件为.h。表示如果帐户不存在,程序就结束,所有查询、取款、存款操作都不允许。
存款操作函数只是完成在帐户中存入一定金额,因此函数需要一个形式参数x表示要存放的金额,但不需要返回任何数据,因此函数为:
void inCome(double x)
{
if(balance==-1){
printf("Account is not exists!n");
exit(0);
}
else
balance=balance+x;
}
取款操作只有余额大于或等于取款金额时,才能完成操作,函数需要“喂入”取款金额,因经需要一个形式参数x,但不需要返回值。函数代码如下:
void outCome(double x)
{
if(balance==-1){
printf("Account is not exists!n");
exit(0);
}
else if( fabs(balance-x)< 1e-6 )
printf("Balance is less than outcome!n");
exit(1);
}
else
balance=balance-x;
}
相关函数完成后,现在该来考虑主函数main了。在程序执行后,希望显示一个菜单给用户并提示用户输入字符做出选择,计算机根据用户选择完成相应的操作。操作完成后继续显示主菜单,等待输入字符继续下一次操作。当输入数值 0时退出循环。main的伪代码为:
int main( )
{
int choice;
double x;
while( 1 ){ //死循环
choice=menu( ); //显示主菜单并返回输入的数
if(choice==0) break;
switch(choice){
case 1: printf("Enter x before create account!:n");
scanf("%lf",&x);
createAcc(x);
break;
case 2:x=query( );
printf("Your balance is %lfn",x);
break;
case 3:printf("Enter what you income:");
scanf("%lf",&x);
inCome(x);
printf("Your balance is %lfn",balance);
break;
case 4: printf("Enter what you outcome:");
scanf("%lf",&x);
outCome(x);
printf("Your balance is %lfn",balance);
break;
default:
printf("Invalid choice!n");
}
}
return 0;
}
main函数中还有主菜单函数menu没有实现,因为主菜单不仅显示菜单,而且要返回用户的选择。因此,该函数无形式参数,但有一个返回值返回用户选择的数。函数如下:
int menu( )
{
int choice;
printf("1.create account!n");
printf("2.query balance!n");
printf("3.income some money!n");
printf("4.outcome some money!n");
printf("0.end program!n");
printf("Please enter 0、1、2、3、4:");
scanf("%d",&choice);
return choice;
}
现在,整个程序所有代码已完成,应该是目前我们碰到代码行数最多的程序了,但是如果我们如果逐个函数阅读,程序也只有6个函数而已。程序如下:
#include
#include
double balance=-1;
void createAcc(int x){.....}
double query( ){.....}
void inCome(int x){.....}
void outCome(int x){......}
int menu( ){.......}
int main( )
{
......
}
为篇幅起见,程序中每个函数的代码都省略了。以上程序所有函数都共享了一个全局变量,现实问题中,需要全局变量的程序更多一些。
接下来我们看一下,当全局变量与局部变量具有相同的名字时,如何理解程序呢?先看一下以下例子:
int a;
void fun1( )
{
int a=0;
a=a+1;
printf("fun1:a=%dn",a);
}
void fun2( )
{
a=a+1;
printf("fun2:a=%dn",a);
}
void fun3(int a)
{
a=a+1;
printf("fun3:a=%dn",a);
}
int main( )
{
a=10;
fun1( );
fun2( );
fun3(10);
printf("main:a=%dn",a);
return 0;
}
以上程序有一个全局变量a,四个函数都能共享该变量。程序的运行结果是什么呢?在弄懂程序结果之前,我们先要弄明白C语言的规定: C程序的全局变量存储在数据区,而局部变量存在栈区。所以同名全局变量与局部变量,虽然名字相同但它们是不同的变量。在程序执行过程中,变量作用范围越小,优先级越高。所以,当函数中定义的变量名与全局变量相同时,全局变量会被函数屏蔽,函数内使用的同名变量是函数内定义的变量而不全局变量。
根据局部变量优先的原则,函数main中的a是全局变量,在第一行被赋予了10;main中第二行调用了fun1函数,fun1函数有一个局部变量a,且初值为0,所以函数中的a=a+1用的是局部变量,a的值为1;main函数第三行调用函数fun2,而fun2的变量a是全局变量,执行a=a+1后a变为了11;接下来调用fun3,fun3的实参是全局变量a,其值在fun2刚被更新为11,所以把11传给fun3的形式参数a,因此fun3中的a是形式参数,执行a=a+1后形式参数变为了12;最后执行main中的输出,a的值仍然为11。所以,程序运行后,结果是:
fun1:a=1
fun2:a=11
fun3:a=12
main:a=11
在编程时,除非必要,一般不要什么都用全局变量,因为全局变量可以被所有函数更新,导致很难预测在某个时刻其值是多少,这会使程序变得难以理解。但在追求运行速度的程序中——如游戏软件,可以借助全局变量的使用提高程序的执行速度。
本节对变量的生命周期、局部变量与全局变量进行了介绍,特别是对同名全局变量和局部变量进行了区分,对全局变量优缺点和使用场景进行了初步分析。本节就讲到这里,下次再见!