C语言程序设计第六章函数.ppt
第六章 函 数,6.1 概 述,函数是一段程序,它完成特定的任务,使用它时可用简单的方法为其提供必要的数据,然后自动执行这段程序,计算完毕后能保存计算结果回到程序原来的位置继续计算。,如果把编程比做制造一台机器,函数就好比其零部件可将这些“零部件”单独设计、调试、测试好,用时拿出来装配,再总体调试这些“零部件”可以是自己设计制造/别人设计制造/现在的标准产品而且,许多“零部件”我们可以只知道需向它提供什么,它能产生什么,并不需要了解它是如何工作、如何设计制造的所谓“黑盒子”。,函数用于把较大的计算任务分解成若干个较小的任务使程序人员可以在其他函数的基础上构造程序,而不需要从头做起一个设计得当的函数可以把具体操作细节对程序中不需要知道它们的那些部分隐藏掉,从而使整个程序结构清楚,减轻了因修改程序所带来的麻烦。C语言在设计函数时考虑了效率与易于使用这两个方面。一个C程序一般都由许多较小的函数组成,而不是只由几个比较大的函数组成。一个程序可以驻留在一个文件中,也可以存放在多个文件中。各个文件可以单独编译并与库中已经编译过的函数装配在一起。,编写一个测试软件,main()char ans=y;clrscr();cover();/*调用软件封面显示函数*/password();/*调用密码检查函数*/while(ans=y|ans=Y)question();/*调用产生题目函数*/answers();/*调用接受回答函数*/marks();/*调用评分函数*/results();/*调用结果显示函数*/printf(“是否继续练习?(Y/N)n”);ans=getchar();printf(“谢谢使用,再见!”);,/*定义所用函数*/cover()/*软件封面显示函数*/password()/*密码检查函数*/question()/*产生题目函数*/answers()/*接受回答函数*/marks()/*评分函数*/results()/*结果显示函数*/,在C程序设计中,通常:将一个大程序分成几个子程序模块(自定义函数)将常用功能做成标准模块(标准函数)放在函数库中供其他程序调用将程序中反复使用的程序段写成函数的形式,某些只用一次的程序段往往也写成函数形式 引入函数的优点:减少重复编写程序的工作量使程序便于调试和阅读,例6.1 main()/*主函数*/printstar();/*调用printstar函数画*/print_message();/*调用print _message函数写字*/printstar();/*调用printstar函数画*/,printstar();/*printstar函数*/printf(“n*”);,print_message();/*print_message函数*/printf(“n Hello!”);,运行结果:*Hello!*,说明:程序的执行从main函数开始,由主函数调用其它函数,其它函数也可以相互调用,程序流程最后回到main函数,在main函数中结束整个程序的运行main函数是系统定义的,只能由系统调用所有函数在定义时都是平行的,相互独立(一个函数并不从属于另一个函数),即函数不能嵌套定义,但可以相互调用一个程序可以由一个或多个源程序文件组成,一个(主调)函数可以多次调用多个(被调)函数。同一个函数也可以被一个或多个(主调)函数调用任意多次。下图说明一种调用关系:,函数分类,使用情况,库函数(标准函数):,由系统提供,用户可以直接使用,用户自定义函数:解决用户的专门需要,函数形式,无参函数:,主调函数与被调函数之间没有数据传递,有参函数:,主调函数与被调函数之间有数据传递,任务情况,带返回值,不带返回值,6.2 函数的定义,一般形式:函数类型说明符 函数名(形式参数表)变量说明部分 语句部分,含义:类型说明符:指定函数值的类型。若该项缺省,表示函数值为 int 型。若函数没有值,应写作void.,函数的定义,函数名:标识符,用于标识函数,并用其来调用函数。函数名字有值,它代表函数的入口地址。形式参数表:说明参数的个数和类型,简称形参表。一般来说,计算函数需要多少原始数据,函数的形参表中就有多少个形参,每个形参存放一个数据。变量说明:说明函数中用到的除形参以外的其它变量。函数体:为了完成特定的功能而设计的一个或多个语句。,函数的定义,无参函数的定义一般形式:数据类型 函数名()说明部分 语句,现代风格是:数据类型 函数名(void)说明部分 语句,例如:print_message(),函数的定义,有参函数的定义一般形式:数据类型 函数名(形参表)形参类型说明;说明部分 语句,现代风格是:数据类型 函数名(带类型形参表)说明部分 语句,int max(x,y)int x,y;int z;z=xy?x:y;return(z),数据类型 函数名(形参表)形参类型说明;说明部分语句,int max(int x,int y)int z;z=xy?x:y;return(z),/*现代风格是:函数名(带类型形参表)*/,空函数一般形式:数据类型 函数名(),例如:dummy()特点:调用后什么也不做用处:建立程序结构,该空函数在需要时补充,例6.2 求两个整数中的大者并输出,main()int a=2,b=25;int c;c=max(a,b);printf(“Max is%dn”,c);max(int x,int y)int z;z=xy?x:y;return z;,运行输出:Max is 25,例6.3 求两个实数中的大者并输出,main()float a=1.5,b=2.5;int c;c=max(a,b);printf(“Max is%dn”,c);max(float x,float y)float z;z=xy?x:y;return z;,运行输出:Max is 2(编译通过,结果错误),main()float a=1.5,b=2.5;float c;c=max(a,b);printf(“Max is%fn”,c);max(float x,float y)float z;z=xy?x:y;return z;,将%d改为%f,将int c;改为float c;,运行输出:Max is 2.000000(结果仍然错误),main()float a=1.5,b=2.5;float c;c=max(a,b);printf(“Max is%fn”,c);float max(float x,float y)float z;z=xy?x:y;return z;,将max(float x,float y)改为float max(float x,float y),编译出错,为什么?,6.3 函数的调用,函数定义制造函数函数使用声明(准备使用)调用(使用函数),通过函数调用,两个函数中的数据发生联系。,6.3.1 形式参数和实际参数,形式参数:定义函数时函数名后括号内的变量名称实际参数:主调函数中调用函数时,函数名后括号内的参数(变量,表达式等),例6.4 调用函数时的数据传递main()int a,b,c;scanf(%d,%d,实际参数,形式参数,a bx y,说明,形参在函数调用前不占用存储单元,在调用时被分配,调用后释放所占用的存储单元;形参只能是变量,而实参可以是常量、变量或表达式,但要求它们有确定的值。调用时将实参的值赋给形参。在函数中必须指定形参的类型;实参和形参个数相等,按顺序对应,一一传递,实参和形参的类型应相同或赋值兼容;,语言规定,实参对形参的数据传递是“值传递”,即单向传递,只由实参传给形参,而不能由形参传回来给实参。在内存中,实参单元与形参单元是不同的单元。,void fun(int i,int j)int x=7;printf(i=%d,j=%d,x=%dn,i,j,x);main()int i=2,x=5,j=7;fun(j,6);printf(i=%d,j=%d,x=%dn,i,j,x);,i=7,j=6,x=7,i=2,j=7,x=5,#include void sum(int x,int y,int z)z=x+y;main()int a=1,b=2,c=0;sum(a,b,c);printf(c=%dn,c);,c=?,0,6.3.2 函数的返回值,函数的返回值是通过函数中的return语句获得的。return语句将被调用函数中的一个确定值带回主调函数中去。return语句一般形式:return(表达式);或:return 表达式;或:return;,若return后面带表达式,首先计算表达式的值,表达式的值就是所求的函数值。表达式的类型与函数首部说明的类型一致。,int max(int x,int y)int z;z=(xy)?x:y;return(z);,return(xy?x:y);,一个函数可以含有多个return语句,但当执行到其中一个return语句就返回主调函数。,一个函数可以没有return语句,此时当函数执行到最后一个界限符“”时返回主调函数。,说明,函数值的类型:应当在定义函数时指定函数的类型。,int max(int x,int y)double min(int x,int y)char letter(char c1,char c2),max(int x,int y),int max(int x,int y),语言规定,凡不加类型声明的函数,自动按 int 型处理。,在定义函数时,对函数值声明的类型一般应该和return语句中的表达式类型一致;若不一致,则以函数类型为准(自动转换)。对于有返回值的函数,若return语句后面没有表达式,或没有return语句,此时带回一个不确定的返回值。为了明确表示“不带回值”,可以用void定义“无类型”(或称“空类型”)。,void stars(int n),printstar()printf(*);main()int a;a=printstar();printf(%d,a);,例 函数带回不确定值,输出:10,void printstar()printf(*);main()int a;a=printstar();printf(%d,a);,编译错误!,例 函数返回值类型转换,main()float a,b;int c;scanf(%f%f,运行输出:1.5 2.5 Max is 2,6.3.3 函数调用的一般形式,函数名(实参1,实参2,实参n),执行过程:计算各个表达式,得到值v1,vn,把v1,vn赋给对应的形参。然后转去执行函数体,函数体执行结束后,即遇到return语句或执行完函数的最后一个语句,返回到函数调用处。,说明,实参与形参 多个实参间用逗号隔开 实参与形参间个数相等,类型应一致 实参与形参按顺序对应,一一传递数据 实参表求值的顺序与系统有关,调用方式:函数语句:由函数调用加上分号构成,在主调函数中可作为一个独立的语句 stars(20);print_message();函数表达式:函数调用作为一个运算对象出现在表达式中,此时要求函数带回一个确定的值以参加表达式的运算 c=max(a,b);函数参数:函数调用作为另一个函数的实参,其值作为一个实际参数传给被调函数的形参进行处理;此时也要求函数带回一个确定值 m=max(a,max(b,c);,调用位置首先被调用函数必须是已存在的函数,如用户自定义函数或库函数。如果使用库函数,需要在文件的开头用#include 命令将需要的库函数包含到文件中。#include 调用同一源文件中的非标准函数时,必须在主调函数中对所调函数进行声明:函数声明语句(函数原型),6.4 函数原型,一般形式:类型标识符 被调函数名(形参类型 形参名);功能:通知编译程序函数值是什么类型,有多少参数及它们各自的类型,为编译程序进行类型检查提供依据例如:double power(int x,int n);或 double power(x,n);或 double power();,例6.5main()float add();float a,b,c;scanf(“%f,%f”,/*对被调用函数的说明*/,作为表达式被调用,/*定义add函数*/,运行结果:3.6,5.5sum is 9.100000,函数定义和函数声明的区别,函数的定义是确定函数的功能,包括函数名,函数值类型,形参及类型和函数体全部内容。函数的声明只是对要被调用的函数的返回值的类型进行说明,它只包括函数名、函数类型或形参类型,不包括函数体。,三种情况下可以省略声明:函数值是整型(int)或字符型(char)时系统自动按整型说明;所调函数的定义出现在主调函数之前时;文件一开头,在所有函数之前,对所用函数作了声明,无函数说明,整型函数,main()int a,b,c;scanf(“%d,%d”,函数值是整型(int)或字符型(char)时系统自动按整型说明,被调函数在主调函数之前,主调函数在被调函数之后,被调用函数出现在主调用函数之前。float add(float x,float y)float z;z=x+y;return(z);main()float a,b;scanf(%f,%f,在所有函数之前说明函数类型,此处不必说明,定义letter函数,文件一开头,对所用函数作了声明 char letter();main().char letter(c1,c2)char c1,c2;.,main()float a=1.5,b=2.5;float c;c=max(a,b);printf(“Max is%fn”,c);max(float x,float y)float z;z=xy?x:y;return z;,例6.3 求两个实数中的大者并输出,运行输出:Max is 2.000000(编译通过,结果错误),因为函数没声明!,main()float max(float x,float y);float a=1.5,b=2.5;float c;c=max(a,b);printf(“Max is%fn”,c);float max(float x,float y)float z;z=xy?x:y;return z;,运行输出:Max is 2.500000,main()float max(float x,float y);float a=1.5,b=2.5;float c;c=max(a,b);printf(“Max is%fn”,c);float max(float x,float y)float z;z=xy?x:y;return z;,函数原型,附:调用外部函数(其他源文件中定义的函数)时 函数说明语句 extern 函数名();例 文件file1.c中 main()int x=80,y=90,c;extern max();/*函数说明*/c=max(x,y)+20;/*调用max函数*/printf(“Max is%dn”,c);文件files2.c中(与file1.c同目录)extern max(int a,int b)/*extern可省略*/float c;c=ab?a:b;return c;,6.5 函数的嵌套调用,C语言允许嵌套调用函数,即在调用一个函数的过程中,又调用另一个函数,主函数()声明a函数 调用a函数,a函数()声明b函数 调用b函数,b函数(),6.6 函数的递归调用,C语言允许递归调用函数,即在调用一个函数的过程中,又直接或间接的调用该函数本身,直接递归调用 调用函数的过程中又调用该函数本身,int f(int x)int y;y=f(x);return y;,间接递归调用 调用f1函数的过程中调用f2函数,而f2中又需要调用f1。,显然,以上均为无终止递归调用。为此,一般要用if语句来控制使递归过程到某一条件满足时结束。,递归法:类似于数学证明中的反推法,从后一结果与前一结果的关系中寻找其规律性。,归纳法可以分为:递推法 从初值出发,归纳出新值与旧值间直到最后值为止存在的关系要求通过分析得到:初值+递推公式编程:通过循环控制结构实现(循环的终值是最后值)递归法 从结果出发,归纳出后一结果与前一结果直到初值为止存在的关系要求通过分析得到:初值+递归函数,编程:设计一个函数(递归函数),这个函数不断使用下一级值调用自身,直到结果已知处选择控制结构其一般形式是:在主函数中用终值n调用递归函数,而在递归函数中:递归函数名f(参数x)if(n=初值)结果=;else 结果=含f(x-1)的表达式;返回结果(return);,例6.6 用递归法求n!,实际上,递归程序分两个阶段执行回推(调用):欲求n!先求(n-1)!(n-2)!1!若1!已知,回推结束。递推(回代):知道1!2!可求出3!n!,main()int n;float s;float fac();printf(Input n=);scanf(%d,float fac(int x)int f;if(x=0|x=1)f=1;else f=fac(x-1)*x;return f;,运行:Input n=55!=120,例6.7 有5个人,第5个人说他比第4个人大2岁,第4个人说他对第3个人大2岁,第3个人说他比第2个人大2岁,第2个人说他比第1个人大2岁,第1个人说他10岁。求第5个人多少岁。,图示,程序:,main()clrscr();printf(%d,age(5);age(int n)int c;if(n=1)c=10;else c=age(n-1)+2;return c;,运行:18,1 1 1 1 2 1 1 3 3 1 1 4 6 4 11 5 10 10 5 1,例6.8在屏幕上显示杨辉三角形,程序如下:main()int i,j,n;clrscr();printf(Input n=);scanf(%d,int c(int x,int y)int z;if(y=1|y=x)return 1;else z=c(x-1,y-1)+c(x-1,y);return z;,#include rev()char c;c=getchar();if(c=$)printf(%c,c);else rev();printf(%c,c);main()rev();,例6.9运行下列程序,当输入字符序列AB$CDE并回车时,程序的输出结果是什么?,运行:$BA,例6.10反向输出一个整数(非数值问题),非数值问题的分析无法象数值问题那样能得出一个初值和递归函数式,但思路是相同的。分析方法:简化问题:设要输出的正整数只有一位,则“反向输出”问题可简化为输出一位整数。对大于10的正整数,逻辑上可分为两部分:个位上的数字和个位以前的全部数字。将个位以前的全部数字看成一个整体,则为了反向输出这个大于10的正整数,可按以下步骤:a、输出个位上的数字;b、将个位除外的其他数字作为一个新的整数,重复a步骤的操作。,其中b问题只是对原问题在规模上进行了缩小递归。所以,可将反向输出一个正整数的算法归纳为:if(n为一位整数)输出n;else 输出n的个位数字;对剩余数字组成的新整数重复“反向输出”操作;,程序如下:#include void main()void printn(int x);int n;printf(Input n=);scanf(%d,void printn(int x)if(x=0,例6.11汉诺塔(Tower of Hanoi)问题,要求:把64个盘子从A座移到C座规则:每次只能移动一个盘在移动过程中在三个座上始终保持大盘在下,小盘在上移动中可利用B座,分析方法:简化问题:设盘子只有一个,则本问题可简化为ac。对于大于一个盘子的情况,逻辑上可分为两部分:第n个盘子和除n以外的n-1个盘子。如果将除n以外的n-1个盘子看成一个整体,则要解决本问题,可按以下步骤:将a座上n-1个盘子移到b座将a座上第n个盘子移到c座将b座上n-1个盘子移到c座,void move(char getone,char putone)printf(%c-%cn,getone,putone);,void hanoi(int n,char one,char two,char three)if(n=1)move(one,three);else hanoi(n-1,one,three,two);move(one,three);hanoi(n-1,two,one,three);,main()int m;printf(Input the number of disks:);scanf(%d,Input the number of disks:3The steps to moving 3 disks:A-CA-BC-BA-CB-AB-CA-C,递归的条件:,1、须有完成函数任务的语句;,long f(int n)long y;if(n=1)y=1;else y=n*f(n-1);return(y);,2、一个确定是否能避免递归调用的测试;,3、一个递归调用语句;该语句的参数应该逐渐逼近结束条件,以至最后断绝递归。,4、先测试,后递归调用。,在递归函数定义中,必须先测试,后递归调用。也就是说,递归调用是有条件的,满足了条件后,才可以递归。,long f(int n)long y;y=n*f(n-1);if(n=1)y=1;return(y);,递归的特点:,1、递归调用不是重新复制该函数,每次调用它时,新的局部变量和形参会在内存中重新分配内存单元,并以新的变量重新开始执行;每次递归返回时,当前调用层的局部变量和形参被释放,并返回上次调用自身的地方继续执行;,2、递归调用一般并不节省内存空间,因为每次调用都要产生一组新的局部变量,从而不破坏上层的局部变量;,3、递归调用一般并不能加快程序的执行速度,因为每次调用都要保护上层局部量(现场),而返回时又要恢复上层局部量,占用执行时间;,4、递归函数中,必须有结束递归的条件;,5、递归调用的优点是能实现一些迭代算法难以解决的问题。,6.7 数组作为函数参数,数组元素可以作函数的实参数组名可以作函数的实参和形参多维数组可以作函数参数6.7.1 数组元素作函数的实参 由于表达式可以做实参,数组元素可以作为表达式的组成部分,因此,数组元素可以做函数的实参,并且可以单向传递给形参。,例 两个数组大小比较,n=0m=0k=0,a和b为有10个元素的整型数组比较两数组对应元素变量n,m,k记录aibi,ai=bi,aik,认为数组ab 若nk,认为数组ab 若n=k,认为数组a=b,数组名作函数参数地址传递在主调函数与被调函数分别定义数组,且类型应一致形参数组大小(多维数组第一维)可不指定形参数组名是地址变量,例 求学生的平均成绩,#include float average(int stu10,int n);void main()int score10,i;float av;printf(Input 10 scores:n);for(i=0;i10;i+)scanf(%d,float average(int stu10,int n)int i;float av,total=0;for(i=0;in;i+)total+=stui;av=total/n;return av;,实参用数组名,形参用数组定义,int stu,说明:,1、用数组名作函数参数,应在主调函数和被调函数中分别定义数组,而不能只在一方定义,被调函数是在形参表中进行定义;,float score10;,aver=average(score);,float average(float array10),2、形参数组和实参数组的大小可一致可不一致;语言编译时对形参数组大小不作检查,只将实参数组的首地址传给形参数组;,3、形参数组可以不指定大小,仅给出数组类型、数组名和一对方括号,另设一个参数传递数组的实际长度;,float average(float array,int n),aver=average(score,10);,4、实参数组与形参数组类型应一致,否则出错;,5、数组名作函数实参时,不是把数组的值传递给形参,而是把实参数组的起始地址传递给形参数组;这样,形参数组和实参数组就共占同一段内存单元,即表示同一个数组。因此,形参数组元素的变化等同于实参数组元素的变化;,6、从传递方式来看,应理解为“传值”方式,即传递的是数组名所代表的值。,例 数组元素与 数组名 作函数参数比较,#include void swap2(int x,int y)int z;z=x;x=y;y=z;main()int a2=1,2;swap2(a0,a1);printf(a0=%dna1=%dn,a0,a1);,值传递,#include void swap2(int x)int z;z=x0;x0=x1;x1=z;main()int a2=1,2;swap2(a);printf(a0=%dna1=%dn,a0,a1);,地址传递,例 数组元素与 数组名 作函数参数比较,例6.12用选择法对数组中10个整数按由小到大排序,所谓选择法就是:先将10个整数中最小的数与a0对换,再将a1到a9中最小的数与a1对换;.每比较一轮,找出未经排序的数中最小的一个,共应比较 9 轮。,程序设计函数sort(array,n)对数组元素按由小到大排序主程序:输入array数组,调用 sort 函数比较,输出排序后的array数组,排序函数:无返回值,数组名为形参形参数组说明可以不指定大小,交换arrayj和 arrayi;,void sort(int array,int n)int v,j,t;for(i=0;in-1;i+)for(j=i+1;jn;j+)if(arrayjarrayi)t=arrayi;arrayi=arrayj;arrayj=t;,main()int a10,i;printf(“enter array:n”);for(i=1;i10;i+)scanf(“%d”,实参数组说明,数组 a 赋值,调用排序函数,由于地址传递,实参数组 a 改变,多维数组作函数参数,形参数组定义时可以指定每一维的大小,也可省略第一维的大小说明(但不能省略第二维及其它高维的大小说明)例如:int array 310;或 int array 10;但不能写成 int array 3;和 int array;实参数组可以大于形参数组例如:实参数组定义为:int array 510;形参数组定义为:int array 310;形参数组只取实参数组的一部分,其余部分不起作用,例 写函数打印n行10列二维数组,其中n是任意整数 void print_array(int a 10,int n)int i,j;for(i=0;in;i+)for(j=0;j10;j+)printf(“%3d”,aij);printf(“n”);例 写函数交换任意一个n*10的二维数组的i,j两行 void exchange(int a 10,int i,int j)int k,t;for(k=0;k10;k+)t=aik;aik=ajk;ajk=t;,例8.12 n行10列int型数组的每一行都有一个最大值,写一函数,求这n个最大值的最小值.max_element(int a,int m)int max,i;max=a0;for(i=1;imax)max=ai;return max;max_min(int a,int n)int i,min,max;min=max_element(a0,10);for(i=1;imax)min=max;return min;,例 求二维数组中最大元素值,int max_value(int array34)int i,j,k,max;max=array00;for(i=0;imax)max=arrayij;return(max);main()int a34=1,3,5,7,2,4,6,8,15,17,34,12;printf(max value is%dn,max_value(a);,例 求二维数组中各行元素之和,get_sum_row(int x3,int result,int row,int col)int i,j;for(i=0;irow;i+)resulti=0;for(j=0;jcol;j+)resulti+=xij;main()int a23=3,6,9,1,4,7;int sum_row2,row=2,col=3,i;get_sum_row(a,sum_row,row,col);for(i=0;irow;i+)printf(The sum of row%d=%dn,i+1,sum_rowi);,6.8 变量的作用域与存储类别,作用域,定义:指变量能够起作用的程序范围。如果一个变量在某源程序文件或某函数范围内有效,则称该文件或函数为该变量的作用域。,局部变量,全局变量,从作用域角度,生存期,定义:指变量在内存中存在的时间范围。,静态变量,动态变量,从生存期角度,变量在整个程序的运行时间都存在,变量只在某个函数的执行过程中才存在,6.8.1 局部变量与全局变量,局部变量函数内部或复合语句内定义的变量称为局部变量,例:,int f1(int a)int v1,x;int f2(void)int v2,x,y;main()int m,y;,a、v1、x的有效范围,v2、x、y的有效范围,m、y的有效范围,说明:,、主函数main中定义的变量也只在主函数中有效,其它函数不能引用;、不同函数中可以使用相同名字的变量,它们代表不同的对象,占用不同的内存单元,互相独立;、形式参数也是局部变量;、可以在复合语句中定义变量,其作用域只是本复合语句。,main()int a,b;int c;c=a+b;,c在此范围内有效,a、b在此范围内有效,void main()int i,a=0;for(i=1;i=2;i+)int a=1;a+;printf(i=%d,a=%dn,i,a);printf(i=%d,a=%dn,i,a);,i=1,a=2,i=2,a=2,i=3,a=0,全局变量,定义:在函数外部定义的变量,又称为外部变量。全局变量可以为本文件中其它函数所共用其有效范围为从定义变量的位置开始到本源文件结束,例:,#include int p=1,q=5;float f1(int a)int b,c;char c1,c2;char f2(int x,int y)int i,j;.main()int m,n;,全局变量p、q的作用域,全局变量c1、c2的作用域,说明:,、全局变量增加了函数间的数据联系;、尽量少使用全局变量(除非在必要时);,各模块间的相互联系、相互影响太多,降低了模块的独立性;会降低程序的清晰性,因为各个函数都有可能改变全局变量的值,需要时刻记住变量的当前值,编程时候容易出错。,#include int k;void show()for(k=1;k=10;k+)putchar(*);putchar(n);,main()for(k=1;k=4;k+)show();,、若全局变量与局部变量同名,则在局部变量的作用范围内,全局变量不起作用;,#include int a=3,b=5;int max(int a,int b)int c;c=ab?a:b;return(c);void main()int a=8;printf(%d,max(a,b);,形参a、b的作用域,全局变量a、b不起作用,局部变量a的作用域,全局变量b的作用域,全局变量a不起作用,讨论:如果主函数中没有int a=8,结果?,、若全局变量在文件开头定义,则在整个程序中都可以使用;若不在开头定义,其作用域只限于说明处到文件结束。如果想在定义之前的函数中引用该全局变量,则在函数中用关键字“extern”作“外部变量声明”,在函数内部,从声明之处起,可以使用它们。,#include int max(int x,int y)int z;z=xy?x:y;return(z);void main()extern int a,b;printf(%d,max(a,b);int a=13,b=-8;,6.8.2 变量的存储类型,结构化设计要求研究不同模块(函数、源文件)间变量的关系。变量两大属性:数据类型 存储类别,1、数据类型(复习)通过变量声明(定义)来规定其数据类型:格式 数据类型关键字 变量名 如 char a;int b,c;float x,y;,预留存储空间(如char型为1个字节,int型为2个字节)确定存储方式(如char型存放ASCII值,int型存放补码值)char型 01100001(用一个字节存放该字符的ASCII值)int 型(用两个字节存放该数值的补码)float型 0110101010 011000011(四个字节)一个变量的数据类型定义后,就规定了该变量只能存储相应类型的数据,2、存储空间的划分,程序区:用于存放程序编译后形成的可执行代码(执行时装入),静态存储区:用于存放程序中的静态数据,如全局变量等,动态存储区:用于存放程序中的动态数据,如函数形参、局部变量、函数调用时的现场保护和返回地址等,静态数据说明时在静态存储区中分配存储单元并在程序执行过程中始终占用该单元,直到程序结束才释放;动态数据在函数开始执行时分配动态存储空间,函数结束时释放这些空间。,3、存储类别 规定了变量在计算机内部的存放位置决定变量的“寿命”(何时“生”,何时“灭”)一个完整的变量说明格式如下:存储类别 数据类型 变量名 如 static int x,y;的存储类别有四种:auto、static、register和extern。,register型(寄存器型)变量值存放在运算器的寄存器中存取速度快,一般只允许23个,且限于char型和int型,通常用于循环变量,auto型(自动变量型)变量值存放在主存储器的动态存储区(堆栈方式)优点同一内存区可被不同变量反复使用,static型(静态变量型)变量值存放在主存储器的静态存储区程序执行开始至结束,始终占用该存储空间 extern型(外部变量型)同上,其值可供其他源文件使用,以上两种均属于“动态存储”性质,即调用函数时才为这些变量分配单元,函数调用结束其值自动消失。,以上两种均属于“静态存储”性质,即从变量定义处开始,在整个程序执行期间其值都存在。,4、局部变量的存储方式,函数中的局部变量,如不做专门的声明,都是动态分配存储空间的,存储在动态存储区中;用关键字auto作存储类型的声明。,auto int b,c=3;,auto float f;,通常auto被省略,auto不写则隐含确定为“自动存储类别”,int b,c=3;float f;,局部静态变量用static声明变量为“局部静态变量”。如 static int s;static char ch;,void f(int c)int a=0;static int b=0;a+;b+;printf(%d:a=%d,b=%dn,c,a,b);void main()int i;for(i=1;i=3;i+)f(i);,1:a=1,b=1,2:a=1,b=2,3:a=1,b=3,说明:,、静态局部变量采用静态存储方式,而自动变量采用动态存储方式;,、若对变量赋初值,对于静态变量,只执行一次,再次调用函数时不再赋初值而保留上次函数调用结束时的值;而对于自动变量,每次调用都要重新分配内存单元并赋初值;,、若不对变量赋初值,对于静态变量系统自动赋缺省值;而对于自动变量,只分配存储单元,其值不确定。,、虽然静态局部变量在函数调用结束后仍占存储单元,但由于是局部变量,其它函数不能引用它。,适用范围:,、需要保留函数上一次调用结束时的值;(占用永久性的存储空间),、对于数组进行初始化,通常定义为静态存储类别。,寄存器(register)变量,为了减少从内存中存取变量值的时间,语言允许将局部变量的值放在寄存器中