程序设计技术.ppt
程序设计技术程序设计语言和算法描述C程序设计入门C程序的控制结构函数与程序结构指针与函数构造数据类型与指针位运算文件C语言应用第四章模块化程序设计基础4.1模块化基本概念4.2程序设计中实现模块化的方法4.3程序设计中标识符的作用域和生存期4.4递归方法的实现4.1模块化基本概念模块化概念 模块化就是将一个大的复杂的程序根据模块化就是将一个大的复杂的程序根据“分而治之,分而治之,各个击破各个击破”的原则划分成为若干个模块,的原则划分成为若干个模块,每个模块完每个模块完成一个相对独立的子功能,将这些模块按成一个相对独立的子功能,将这些模块按某种方式搭某种方式搭接起来就构成了解决总体问题的程序。接起来就构成了解决总体问题的程序。4.1模块化基本概念对软件进行模块化可以得到如下好处:(1)软件具有模块化结构,所有软件开发工作可以如同搭软件具有模块化结构,所有软件开发工作可以如同搭积木积木那样,把各个功能模块进行组合。模块相对独立,任一那样,把各个功能模块进行组合。模块相对独立,任一模块中模块中的错误不易扩散到其他模块中去,从而提高了软件开发的错误不易扩散到其他模块中去,从而提高了软件开发的效率的效率和软件的可靠性。和软件的可靠性。(2)由于模块的功能是单一完整、相对独立的,所以每个由于模块的功能是单一完整、相对独立的,所以每个模块都可以单独地设计它的算法,单独进行编写和调模块都可以单独地设计它的算法,单独进行编写和调试。对于大型软件的开发,采用模块化结构,可以由试。对于大型软件的开发,采用模块化结构,可以由多个程序设计人员参与进行集体性开发,从而加快软多个程序设计人员参与进行集体性开发,从而加快软件开发速度、缩短软件开发周期。件开发速度、缩短软件开发周期。(3)采用模块化程序设计技术开发的计算机软件投入运行采用模块化程序设计技术开发的计算机软件投入运行后,在进行软件维护时,能够进行整个系统的调试,后,在进行软件维护时,能够进行整个系统的调试,也可以进行单一模块的调试。对单一模块的调试和修也可以进行单一模块的调试。对单一模块的调试和修改不会影响到其它模块的功能。当软件系统需要扩充改不会影响到其它模块的功能。当软件系统需要扩充时,只需增加相应功能模块,而不会涉及到整个软件时,只需增加相应功能模块,而不会涉及到整个软件系统。因此,采用模块化程序设计技术开发的计算机系统。因此,采用模块化程序设计技术开发的计算机软件具有良好的维护性。软件具有良好的维护性。4.1模块化基本概念信息隐蔽和局部化概念信息的隐蔽指的是在设计和确定模块时,应该使得模块内包含的所有信息(数据和执行代码)对于那些不需要这些信息的其他模块来说是不可访问的。也就是说,应该将模块与模块之间的关系尽可能限制在最小的范围之内。信息的局部化指的是在划分和确定模块时,将那些关系密切的软件元素在物理位置上尽可能靠近。例如,在程序设计对数据元素的局部化以保证数据元素只能在程序的局部范围内使用。4.2程序设计中实现模块化的方法#includestdio.hvoidmain()inta,b;intfac(int);for(a=1;a=5;a+)b=fac(a);printf(%d!=%dn,a,b);int fac(int c)int fac(int c)int m,s;int m,s;s=1;s=1;for(m=1;m=c;m+for(m=1;m=c;m+)s=s*m;s=s*m;return(s);return(s);4.2程序设计中实现模块化的方法一、函数的定义和声明C语言函数定义的现代风格形式如下:返回值类型说明符函数名(形式参数表及其说明)函数的操作对象(数据)定义和说明部分函数的执行语句部分4.2程序设计中实现模块化的方法类型名类型名 函数名(类函数名(类型名型名 形参形参1,类型名,类型名 形参形参2,)说明语句;说明语句;执行语句;执行语句;函数首部(函数头)函数体例:两个数之和的函数。doubleadd(doublex,doubley)doublez;z=x+y;return(z);函函数数体体:中的内容,包括说明语句和执行语句。空空函函数数:函数体为空的函数,例,便于扩充和细化程序。注:(1)函数的定义是平行的,不允许在一个函数的内部再定义一个函数。(2)函数值的类型为int或char时,可省略;不需返回函数值时,可用类型名void。(3)多个形参以逗号分隔。(4)不同函数中的局部变量可以同名。4.2程序设计中实现模块化的方法从上面例子可以看出函数的定义内容为:1函数类型(既函数值类型)2函数名3形式参数的数目、类型4函数体内容4.2程序设计中实现模块化的方法函数的返回值类型说明符规定了函数返回值的函数的返回值类型说明符规定了函数返回值的数据类型。例如,上面例子中数据类型。例如,上面例子中double数据类型数据类型说明了函数说明了函数fac被执行后可以得到一个被执行后可以得到一个double数数据类型的数据。据类型的数据。C语言规定,如函数的返回值是整型(int)或者是字符型(char)时,函数的返回值类型说明符可以缺省;如果所定义的函数没有(或不需要有)返回值,则函数的返回值类型为void(空类型)。4.2程序设计中实现模块化的方法函数名函数名是为所定义的函数取的名字,函数名是函数名是为所定义的函数取的名字,函数名是C C语言中语言中合法的标识符。函数名后的圆括号是函数的标志,合法的标识符。函数名后的圆括号是函数的标志,即即使函数没有形式参数也不能省略。除主函数使函数没有形式参数也不能省略。除主函数mainmain()()外,其余的函数名均可以由用户自己取名,但在外,其余的函数名均可以由用户自己取名,但在同一同一程序的各个源文件中的函数不能重名。程序的各个源文件中的函数不能重名。4.2程序设计中实现模块化的方法形式参数函数定义时填入的参数我们称之为形式参数,简称形参,它们同函数内部的局部变量作用相同。形式参数写在函数名后面的一对圆括号内,它主要有两个作用:(1)表示将从主调函数中接收的信息。函数的形式参数及其说明表是为函数接收外来数据提供变量名称以便在函数中使用,它规定了传递数据的类型和数目。如:int f(int a,float b)表示将从主调函数中接收一个int型和一个float型数据。形式参数之间应以调号相隔。无形式参数时,圆括号可以为空,也可以使用void声明它为空。如 int f()与 int f(void)两者完全等价,但是圆括号不能省掉。(2)在函数体中形式参数是可以被引用的,可以输入、输出、被赋以新值或参与运算。4.2程序设计中实现模块化的方法函数体函数体是由变量定义部分和C语句组成。在函数体中定义的变量只有在执行该函数时才存在。函数体中也可以不定义变量,而只有语句。也可以二者皆无。如:void nothing()这是一个空函数,调用它不产生任何有效操作,但却是一个符合C语言语法的合法函数。4.2程序设计中实现模块化的方法函数执行的最后一个操作是返回。函数返回的基本格式有如下两种:第一种即有返回值格式:return;第二种即无返回值格式:(函数必须定义为void类型,也可省略返回语句return)return;对于第一种有返回值的语句其执行过程如下:先计算出表达式的值;若表达式的类型和函数类型不同时,将表达式的类型自动转换为函数类型,这种转换是强制性的,可能出现不保值的现象;将计算出表达式的值传给主调函数,然后将程序控制权交给主调函数。此时主调函数接管控制权,继续执行主调函数后的语句。对于第二种无返回值的语句其执行过程如下:当被调函数执行到return;语句时,若无return语句,在执行完函数的最后一个语句之后,从概念上讲,是遇到了函数的结束符“”(当然这个花括号实际上并不会出现在目标码中,但我们可以这样理解),将程序控制权交回给主调函数,此时主调函数接管控制权,继续执行主调函数中调用该被调函数的后继语句。有时在函数中设立了多个终止点以简化函数、提高效率。切记,一个函数可以有多个返回语句,但函数执行到任何一个return语句都会将程序控制权交给主调函数。此时主调函数接管控制权,继续执行主调函数后的语句。如下所示,函数在s1、s2相等时返回1,不相等时返回-1。int find_char(char s 1,char s 2)if(s1=s2)return 1;elsereturn-1;4.2程序设计中实现模块化的方法函数的申明在主调函数中,要对本函数将要调用的函数的特在主调函数中,要对本函数将要调用的函数的特征事征事先进行必要的声明。所谓先进行必要的声明。所谓“声明声明”是指向编译系是指向编译系统提供统提供必要的信息:函数名,函数的返回值的类型,函必要的信息:函数名,函数的返回值的类型,函数参数参数的个数、类型及排列次序,以便编译系统对函数的个数、类型及排列次序,以便编译系统对函数的数的调用进行检查。例如,检查形参与实参类型是否调用进行检查。例如,检查形参与实参类型是否一一致,使用函数返回值的类型是否正确。致,使用函数返回值的类型是否正确。4.2程序设计中实现模块化的方法(1)如何申明系统函数的申明系统函数的申明 使使用用标标准准的的库库函函数数时时,由由于于系系统统定定义义的的标标准准库库函数的说明都函数的说明都集集中中在在一一些些称称为为“头头文文件件”的的文文本本文文件件中中,在在程程序中如果要调用序中如果要调用系系统统的的标标准准库库函函数数时时,也也要要在在程程序序的的适适当当位位置置写写上:上:#includeinclude 或或#include include“相相应应头头文文件件”将将调用有关库函数调用有关库函数时的必要信息包含的本源文件中来。例如:时的必要信息包含的本源文件中来。例如:#include include 或或#inlcude inlcude“stdio.h”“stdio.h”用户自定义函数的申明用户自定义函数的申明4.2程序设计中实现模块化的方法自定义的函数其声明的一般格式为:类型标识符 函数名(类型标识符 形参,类型标识符 形参,);这些信息就是函数定义中的第一行(称函数头)的内容,也称函数模型(或函数原型)。设有一函数的定义为:double funa(double a,int b,float c)函数体4.2程序设计中实现模块化的方法正确完整的函数声明应为:double funa(double a,int b,float c);(注意末尾的分号)这里的形参的名字并不重要,重要的是形参类型,故函数声明又可以不写形参名,即 double funa(double,int,float);但不能只写形参名,不写形参的类型。如:double funa(a,b,c);(错)一般也不能不写函数的类型,如:funa(double a,int b,float c);(错)只有函数返回值为I n t或char时,函数标识符才可以省掉。形参的次序也不能写错,如:double funa(int b,float c,double a);(错)4.2程序设计中实现模块化的方法例4-1对被调函数的声明示例。#includevoidmain()longfac(intx);/*对函数fac的声明*/intn;printf(Inputthenumbern:);scanf(%d,&n);printf(%d!=%ldn,n,fac(n);longfac(intx)/*函数fac的定义*/longy;for(y=1;x0;-x)y*=x;returny;4.2程序设计中实现模块化的方法(2)何时不对被调函数进行声明:被调函数的返回值数据类型是整型或字被调函数的返回值数据类型是整型或字符型时,此时系统自动按整型进行隐式符型时,此时系统自动按整型进行隐式声明;声明;被调函数的定义出现在主调函数之前时,被调函数的定义出现在主调函数之前时,其原因是编译系统此时已经知道了被调其原因是编译系统此时已经知道了被调函数的所有特征;函数的所有特征;4.2程序设计中实现模块化的方法例4-2对被调函数不必声明的示例。#include long fac(int x)/*函数函数fac的定义出现在主调函数之前的定义出现在主调函数之前*/long y;for(y=1;x0;-x)y*=x;return y;void main()int n;printf(Input the number n:);scanf(%d,&n);printf(%d!=%ldn,n,fac(n);4.2程序设计中实现模块化的方法(3)申明的位置在所有的函数之前;在所有函数的外部;在调用函数的内部的说明部分;4.2程序设计中实现模块化的方法函数调用时的参数传递方式传值调用传引用(传地址值)调用传值调用方式传值方式是一种数据复制的方式,在这种方式下,实际参数通过复制的方式传递给形式参数,因此被传递的数据在被调函数中无论怎样变化,都不会影响该数据在主调函数中的值。函数调用时的步骤:函数调用时的步骤:1 1、建立形式参数和局部、建立形式参数和局部变量变量2 2、实际参数拷贝到对应、实际参数拷贝到对应形式参数形式参数3 3、控制转到被调函数执、控制转到被调函数执行行4 4、退出被调函数时撤消、退出被调函数时撤消其形式参数和局部变量其形式参数和局部变量n n二、函数的调用和数据传递二、函数的调用和数据传递二、函数的调用和数据传递二、函数的调用和数据传递 4.2程序设计中实现模块化的方法例4-3函数调用时值参数传递示例。#includeintchange(intx,inty);voidmain()intm=10,n=20;printf(m=%d,n=%dn,m,n);printf(%dn,change(m,n);printf(m=%d,n=%dn,m,n);intchange(intx,inty)x+,y+;printf(“x=%d,y=%dn”,x,y);returnx+y;程序运行的结果为:m=10,n=2032m=10,n=20/*请仔细分析并回答为什么输出这个结果*/4.2程序设计中实现模块化的方法调用时调用时形参形参y形参形参x10201020实参实参m实参实参n形参形参y形参形参x实参实参a实参实参b10201121执执行行完完交交换换后后图图4.24.2程序设计中实现模块化的方法Question?在语言中,可以用几种方式调用函数:(1 1)函数表达式。函数作为表达式的一项,出现在表)函数表达式。函数作为表达式的一项,出现在表达式中,以函数返回值参与表达式的运算。这种方达式中,以函数返回值参与表达式的运算。这种方式要求函数是有返回值的。式要求函数是有返回值的。(2 2)函数语句。函数语句。C C语言中的函数可以只进行某些操作语言中的函数可以只进行某些操作而不返回函数值,这时的函数调用可作为一条独立而不返回函数值,这时的函数调用可作为一条独立的语句。的语句。(3 3)函数实参。函数作为另一个函数调用的实际参数)函数实参。函数作为另一个函数调用的实际参数出现。这种情况是把该函数的返回值作为实参进行出现。这种情况是把该函数的返回值作为实参进行传送,因此要求该函数必须是有返回值的。传送,因此要求该函数必须是有返回值的。4.2程序设计中实现模块化的方法函数的调用应注意的几点:(1 1)调调用用函函数数时时,函函数数名名称称必必须须与与具具有有该该功功能能的自定义函数名称完全一致。的自定义函数名称完全一致。(2 2)实实参参在在类类型型上上按按顺顺序序与与形形参参,必必须须一一一一对对应应和和匹匹配配。如如果果类类型型不不匹匹配配,C C编编译译程程序序将将按按赋赋值值兼兼容容的的规规则则进进行行转转换换。如如果果实实参参和和形形参参的的类类型型不不赋赋值值兼兼容容,通通常常并并不不给给出出出出错错信信息息,且且程序仍然继续执行,只是得不到正确的结果。程序仍然继续执行,只是得不到正确的结果。(3 3)如果实参表中包括多个参数,对实参的求)如果实参表中包括多个参数,对实参的求值顺序随系统而异。有的系统按自左向右顺序值顺序随系统而异。有的系统按自左向右顺序求实参的值,有的系统则相反求实参的值,有的系统则相反。Turbo CTurbo C和和MS MS C C是按自右向左的顺序进行的是按自右向左的顺序进行的 。4.2程序设计中实现模块化的方法例4-4 编程求出所有的两位的“绝对素数”,所谓“绝对素数”是指一个素数,当它的数字位置对换后仍然是素数。解题思想:对于任意两位数解题思想:对于任意两位数n n,首先调用函数首先调用函数primeprime判判断断n n是否是素数,如果是否是素数,如果n n是素数,则调用函数是素数,则调用函数reversereverse求求数字位置对换后的数,函数数字位置对换后的数,函数primeprime判断其是否是素判断其是否是素数,如果数,如果reversereverse(n n)也是素数,也是素数,n n是是“绝对素数绝对素数”,输输出出n n。函数函数primeprime()()是一个判断素数的通用函数,返回值是一个判断素数的通用函数,返回值为为0 0时表示非素数,返回值为时表示非素数,返回值为1 1时表示为素数。其算法时表示为素数。其算法描述如下:描述如下:4.2程序设计中实现模块化的方法算法:prime(a)/*表示prime从主调函数接收a*/算法开始定义标志量b,循环变量k;If(a=2)then返回值 1;else if(a能被2整除)then返回值 0;Else b=1,k=3 while(k=a的平方根 并且b=1)if(a能被k整除)thenb=0 b=0 k=k+2k=k+2 返回返回b b;算法算法结结束束 4.2程序设计中实现模块化的方法reverse(int a)函数是求a各位数字前后位置对换后的整数的通用函数,如12345各位数字前后位置对换后转换为54321,其本思路:采用a%10求出a的个位,然后a=a/10,重复前面的操作,直到a=0。同时注意:如12345转换为54321,而54321=(5*10+4)*10+3)*10+2)*10+1。算法描述如下:算法:reverse(a)算法开始定义 s=0;while(a0)s=s+a%10 a=a/10 if(a0)then s=s*10返回s;算法结束4.2程序设计中实现模块化的方法#include#include void main()int reverse(int a);int prime(int a);int n;for(n=11;n=99;n+)if(prime(n)&prime(reverse(n)printf(“%4d”,n);4.2程序设计中实现模块化的方法intprime(inta)intb,k;if(a%2=0)return(0);elseb=1;k=3;while(k=sqrt(1.0*a)&b)if(a%k=0)b=0;k+=2;return(b);4.2程序设计中实现模块化的方法intreverse(inta)ints=0;while(a!=0)s=s+a%10;a=a/10;if(a)s=s*10;returns;4.2程序设计中实现模块化的方法C语言规定,在C程序中所有的函数都是平行的,即在C程序中函数不能嵌套定义。但C语言允许函数嵌套调用,即一个函数在被调用的过程中又调用了另外的一个函数。一个两层嵌套函数调用的过程如图所示:图中主函数在执行的过程中调用了函数图中主函数在执行的过程中调用了函数f1f1,在函数在函数f1f1执行执行的过程中又调用了函数的过程中又调用了函数f2f2;当执行完函数当执行完函数f2f2后,程序控制后,程序控制流程转回函数流程转回函数f1f1对对f2f2调用语句的后面第一条可执行调用语句的后面第一条可执行C C语句继语句继续执行;当函数续执行;当函数f1f1执行完后,程序控制流程转回主函数继执行完后,程序控制流程转回主函数继续执行。续执行。三、函数的嵌套调用三、函数的嵌套调用三、函数的嵌套调用三、函数的嵌套调用例4-5问题分析:4.2程序设计中实现模块化的方法例4.6 计算s=1k+2k+3k+N k程程序序设设计计思思路路:可可以以把把问问题题分分解解为为两两个个模模块块:求求幂次幂次方方模模块块和和求求和和模模块块,在在求求和和模模块块中中要要包包含含求求幂幂模模块块f1()f1()函数的参数为函数的参数为n n和和k k,其返回值是其返回值是n n的的k k次方次方f2()f2()函函数数的的参参数数为为n n和和k,k,返返回回值值是是幂幂次次方方的的累累加加和。和。4.2程序设计中实现模块化的方法#include long f1(int n,int k);long power=n;int i;for(i=1;ik;i+)power*=n;return power;long f2(int n,int k)long sum=0;int i;for(i=1;i=n;i+)sum+=f1(i,k);return sum;void main()int n,k;scanf(“%d,%d”,&n,&k);printf(Sum of%d powers of integers from 1 to%d=,k,n);printf(%dn,f2(n,k);4.2程序设计中实现模块化的方法递归方法的实现用用“辗转除法辗转除法”求最大公约数时使用了递求最大公约数时使用了递推方法,其算推方法,其算法法和和C函数可以描述为:函数可以描述为:m除以除以n得到余数得到余数r(0rn)。若若r=0则算法结束,则算法结束,n为最大公约数。否则为最大公约数。否则做做。mn,nr,转回到转回到。4.2程序设计中实现模块化的方法函数的递归调用一个函数直接地或间接地自己调用自己,称为函一个函数直接地或间接地自己调用自己,称为函数的数的递归调用。递归调用可以看成是一种特殊的嵌套递归调用。递归调用可以看成是一种特殊的嵌套调调用,它与一般的嵌套调用相比较有以下特点:用,它与一般的嵌套调用相比较有以下特点:(1)每次嵌套调用的函数都是该函数本身;)每次嵌套调用的函数都是该函数本身;(2)嵌套调用不会无限制进行下去,即调用总)嵌套调用不会无限制进行下去,即调用总会在某会在某种条件下结束;种条件下结束;(3)每次调用时,本次的函数体并没有执行完)每次调用时,本次的函数体并没有执行完毕。故毕。故必须依靠系统提供一个特殊部件(堆栈)存放未必须依靠系统提供一个特殊部件(堆栈)存放未完成完成的操作,以保证当递归调用结束回溯时不会丢失的操作,以保证当递归调用结束回溯时不会丢失任何任何应该执行而没有执行操作;应该执行而没有执行操作;4.2程序设计中实现模块化的方法一个问题要用递归的方法解决,必须满足3个条件:(1)要解决的问题可以转换为一个新的问题,而这个新的问题的解法与原来问题的解法相同,只是处理的对象有规律的变化,一般是递减。(2)可以应用这个转换过程使问题得到解决(3)要有一个明确的递归结束条件。4.2程序设计中实现模块化的方法例使用递归调的方法反向输出字符串。#includevoidmain()voidreverse();reverse();voidreverse()charch;ch=getchar();if(ch=#)putchar(ch);elsereverse();/*递归调用*/putchar(ch);4.2程序设计中实现模块化的方法4.2程序设计中实现模块化的方法ch=#ch=cch=bch=amain()输出a输出b输出c输出#(颠倒字符串)4.2程序设计中实现模块化的方法例求两个正整数的最大公约数。分别用迭代和递归的方法实现递归的条件:4.2程序设计中实现模块化的方法/*迭代方式,使用循环结构实现*/#includevoidmain()intGCD(intm,intn);intnum1,num2;printf(Inputnum1&num2:);scanf(%d,%d,&num1,&num2);printf(%dn,GCD(num1,num2);intGCD(intm,intn)intr;while(r=m%n)!=0)m=n;n=r;returnn;4.2程序设计中实现模块化的方法/*递归方式,使用分支结构实现。主函数与上面程序相同递归方式,使用分支结构实现。主函数与上面程序相同*/#include void main()int GCD(int m,int n);int num1,num2;printf(Input num1&num2:);scanf(%d,%d,&num1,&num2);printf(%dn,GCD(num1,num2);int GCD(int m,int n)int r;if(r=m%n)=0)return n;elsereturn GCD(n,r);4.2程序设计中实现模块化的方法例12求一个正整数的各位数字之和分别用迭代和递归的方法实现递归的条件:4.2程序设计中实现模块化的方法/*迭代方式,使用循环结构实现*/#includevoidmain()intnum_sum(intn);intnum;printf(Inputthenum:);scanf(%d,&num);printf(%dn,num_sum(num);intnum_sum(intn)intsum=0;while(n)sum+=n%10;n/=10;returnsum;4.2程序设计中实现模块化的方法/*递归方式,使用分支结构实现。主函数与上面程序相同*/intnum_sum(intn)if(n10)returnn;elsereturnn%10+num_sum(n/10);4.2程序设计中实现模块化的方法例15求斐波纳契数列中第n个数。4.2程序设计中实现模块化的方法/*迭代方式,使用循环结构实现*/#includevoidmain()longfib(intn);intn;printf(Inputthen:);scanf(%d,&n);printf(%ldn,fib(n);longfib(intn)intf1=0,f2=1,f3,i;for(i=2;i=n;i+)f3=f2+f1;f1=f2;f2=f3;returnf3;4.2程序设计中实现模块化的方法longfib(intn)if(n=0|n=1)returnn;elsereturnfib(n-1)+fib(n-2);4.2程序设计中实现模块化的方法例16汉诺塔(TowerofHanoi)问题。4.2程序设计中实现模块化的方法分析(1)考虑A座上有n个盘子的一般情况。将n个盘子从A座移到C座算作一个大问题,可以分解为3个小问题:第一步:将A座上面的n-1个盘子借助C座移到B座上。这是整体粗略的考虑,不管实现的细节。只有这样,C座上空了,A座的第n个盘子才能直接移到C上。第二步:将A座上的第n个盘子移到C上。第三步:再将B座上的n-1个盘子借助A座移到C座上。(2)对大问题和第1步、第3步的小问题作对比,它们都是把若干个盘子从一个座借助一个座移到另一个座上,只是源座、借助座、目标座不同。符合递归的条件1:要解决的问题可以转换为一个新的问题,而这个新的问题的解法和原来问题相同。(3)递归函数可以描述为:hanoi(盘子个数,源座,借助座,目标座)大问题描述为:hanoi(n,left,middle,right)4.2程序设计中实现模块化的方法第一个问题:hanoi(n-1,left,right,middle)第一个问题:hanoi(n-1,middle,left,right)(4)第2步可以编写一个移动函数:move(源座,目标座)4.3程序设计中标识符的作用域和生存期C程序的一般结构如图C程序源文件1函数1声明/定义部分执行部分源文件2源文件n函数2函数m图4.2C程序的一般结构4.3程序设计中标识符的作用域和生存期在一个完整的在一个完整的C程序中,函数将程序划分为若干程序中,函数将程序划分为若干个相个相对独立的区域。对于在不同的区域范畴之内声明对独立的区域。对于在不同的区域范畴之内声明或定或定义的标识符(特别是变量)必须要考虑以下几个义的标识符(特别是变量)必须要考虑以下几个方面方面的问题:的问题:标识符(特别是变量)的作用范围如何确定;标识符(特别是变量)的作用范围如何确定;标识符(特别是变量)的存在期间如何确定;标识符(特别是变量)的存在期间如何确定;怎样使用或限制在同一程序的其它源程序文怎样使用或限制在同一程序的其它源程序文件中定义的标识符(变量);件中定义的标识符(变量);4.3程序设计中标识符的作用域和生存期对于一个标识符的性质可以从两方面进对于一个标识符的性质可以从两方面进行分析,一是其能够起作用的空间范围,行分析,一是其能够起作用的空间范围,即标识符的作用域;二是其值存在的时即标识符的作用域;二是其值存在的时间范围,即标识符的生存期。间范围,即标识符的生存期。4.3程序设计中标识符的作用域和生存期在在C语言中,变量定义的完整形式如下:语言中,变量定义的完整形式如下:存储类别符存储类别符 变量表变量表;数据类型符说明标识符(变量)的取值范围和可数据类型符说明标识符(变量)的取值范围和可以对以对该变量进行的操作;存储类别符用于指定数据对该变量进行的操作;存储类别符用于指定数据对象在象在系统内存中存放的方法。变量的存储类别有四种,系统内存中存放的方法。变量的存储类别有四种,它它们是:自动型(们是:自动型(auto)、)、寄存器型(寄存器型(register)、)、静态静态型(型(static)和外部参照型(和外部参照型(extern)。)。n n标识符的作用域标识符的作用域标识符的作用域标识符的作用域 4.3程序设计中标识符的作用域和生存期标识符的作用域标识符的作用域 从变量的作用域概念上可以将变量分为从变量的作用域概念上可以将变量分为“全局变量全局变量”和和“局部变量局部变量”两类。两类。C语言使用存储类别关键字语言使用存储类别关键字extern和和auto来表示变量的作用域属性。来表示变量的作用域属性。全局变量是指定义在所有函数外面的变全局变量是指定义在所有函数外面的变量,其定义的形式如下:量,其定义的形式如下:extern 变量表变量表;4.3程序设计中标识符的作用域和生存期例全局变量的作用域示例。#include int i;void main()void incre();i+;incre();printf(i=%dn,i);/*程序运行结果为:程序运行结果为:i=2,为为什么?什么?*/void incre()i+;4.3程序设计中标识符的作用域和生存期局部变量是定义在函数内部的变量,局部变量局部变量是定义在函数内部的变量,局部变量的作用域被限定在它们所定义的范围之内,在的作用域被限定在它们所定义的范围之内,在C函数中定义自动变量的地方有三个:函数中定义自动变量的地方有三个:函数形式参数表部分;函数形式参数表部分;函数体内部;函数体内部;复合语句内部;局部变量的存储类别符是复合语句内部;局部变量的存储类别符是auto,所以在所以在C语言中局部变量又称为自动变语言中局部变量又称为自动变量。量。4.3程序设计中标识符的作用域和生存期例局部变量的作用域示例。#include void main()void incre();int i=10;i+;incre();incre();printf(i=%dn,i);/*程序运行结果为:程序运行结果为:i=11,为什么?为什么?*/void incre()int i=20;i+;printf(i=%dn,i);/*函数两次别调用时的运行结果为:函数两次别调用时的运行结果为:i=21,为为什么?什么?*/4.3程序设计中标识符的作用域和生存期intx;voidmain()x+;voidf1()intx=1;intx=2;x+;x+;voidf2()x+;全局变量x的作用域函数内部定义的局部变量x的作用域复合语句内部定义的局部变量x的作用域图4.3全局变量与局部变量作用域重叠示意图4.3程序设计中标识符的作用域和生存期C语言中规定按语言中规定按“定义就近原则定义就近原则”来确定使用来确定使用的变量,具体说就是如下两条原则:的变量,具体说就是如下两条原则:在函数中如果定义有与全局变量同名的局部变量,则在函数中如果定义有与全局变量同名的局部变量,则当程序的控制流程进入到函数的作用范围时,使用在当程序的控制流程进入到函数的作用范围时,使用在函数中定义的局部同名变量;函数中定义的局部同名变量;在程序的一个更小局部范围(复合语句)中如果定义在程序的一个更小局部范围(复合语句)中如果定义有与较大范围(函数局部或全局)变量同名的变量,有与较大范围(函数局部或全局)变量同名的变量,则当程序的控制流程进入到这个小的局部范围时,使则当程序的控制流程进入到这个小的局部范围时,使用在该小局部范围内所定义的局部同名变量;用在该小局部范围内所定义的局部同名变量;4.3程序设计中标识符的作用域和生存期例全局变量与局部变量作用域重叠时使用变量示例。#include void f1();int x;void main()int x=10;int x=20;printf(In compound statement,x=%dn,x);printf(In function main,x=%dn,x);f1();void f1()printf(In function f1,x=%dn,x);程序的运行结果如下所示,请读者自行分析:程序的运行结果如下所示,请读者自行分析:In compound statement,x=20In function main,x=10In function f1,x=04.3程序设计中标识符的作用域和生存期标识符(变量)的生存期与其在存储器中标识符(变量)的生存期与其在存储器中存储的位置存储的位置以及系统为它们分配存储的时机相关。以及系统为它们分配存储的时机相关。C语言中使用关键字语言中使用关键字auto、register、static和和extern规定程序中变量的存储类别。规定程序中变量的存储类别。n n标识符的生存期标识符的生存期标识符的生存期标识符的生存期 4.3程序设计中标识符的作用域和生存期1register关键字用关键字用关键字register限定的变量称为寄存器变量,所限定的变量称为寄存器变量,所谓谓寄存器变量指的是将其值存放在寄存器变量指的是将其值存放在CPU的寄存器中的寄存器中的变的变量。量。在程序设计中如果要使用register存储类型的变量必须理解以下两点:(1)register存储类型只能作用于整型(或字符存储类型只能作用于整型(或字符型)局部型)局部变量,并且这些局部变量不能是函数的形式参数;变量,并且这些局部变量不能是函数的形式参数;(2)使用关键字使用关键字register指定一个局部变量为寄存指定一个局部变量为寄存器型变器型变量时只是表达了使用者的一种愿望,该愿望是否量时只是表达了使用者的一种愿望,该愿望是否能实现取决于使用的系统硬件环境和编译系能实现取决于使用的系统硬件环境和编译系统。统。习题练习(1)求s=a+aa+aaa+aaaa+aaa.a的值,其中a是一个数字。例如2+22+222+2222+22222(此时共有5个数相加),几个数相加由键盘控制。(2)有一分数序列:2/1,3/2,5/3,8/5,13/8,21/13.求出这个数列的前20项之和。习题练习#include”stdio.h”voidmain()inta,n,count=1;longintsn=0,tn=0;printf(pleaseinputaandnn);scanf(%d,%d,&a,&n);printf(a=%d,n=%dn,a,n);while(count=n)tn=tn+a;sn=sn+tn;a=a*10;+count;printf(a+aa+.=%ldn,sn);习题练习#include”stdio.h”void