1、谭浩强c语言谭浩强c语言.txt两个人吵架,先说对不起的人,并不是认输了,并不是原谅了。他只是比对方更珍惜这份感情。 谭浩强c语言【例8.6】Hanoi塔问题 一块板上有三根针,A,B,C。A针上套有64个大小不等的圆盘,大的在下,小的在上。如图5.4所示。要把这64个圆盘从A针移动C针上,每次只能移动一个圆盘,移动可以借助B针进行。但在任何时候,任何针上的圆盘都必须保持大盘在下,小盘在上。求移动的步骤。本题算法分析如下,设A上有n个盘子。如果n=1,则将圆盘从A直接移动到C。如果n=2,则:1.将A上的n-1(等于1)个圆盘移到B上;2.再将A上的一个圆盘移到C上;3.最后将B上的n-1(等
2、于1)个圆盘移到C上。 如果n=3,则:A. 将A上的n-1(等于2,令其为n)个圆盘移到B(借助于C),步骤如下:(1)将A上的n-1(等于1)个圆盘移到C上。(2)将A上的一个圆盘移到B。(3)将C上的n-1(等于1)个圆盘移到B。B. 将A上的一个圆盘移到C。C. 将B上的n-1(等于2,令其为n)个圆盘移到C(借助A),步骤如下:(1)将B上的n-1(等于1)个圆盘移到A。(2)将B上的一个盘子移到C。(3)将A上的n-1(等于1)个圆盘移到C。 到此,完成了三个圆盘的移动过程。 从上面分析可以看出,当n大于等于2时,移动的过程可分解为三个步骤:第一步 把A上的n-1个圆盘移到B上;第
3、二步 把A上的一个圆盘移到C上;第三步 把B上的n-1个圆盘移到C上;其中第一步和第三步是类同的。当n=3时,第一步和第三步又分解为类同的三步,即把n-1个圆盘从一个针移到另一个针上,这里的n=n-1。 显然这是一个递归过程,据此算法可编程如下:move(int n,int x,int y,int z) if(n=1) printf(%c-%cn,x,z); else move(n-1,x,z,y); printf(%c-%cn,x,z); move(n-1,y,x,z); main() int h; printf(ninput number:n); scanf(%d,&h); printf(
4、the step to moving %2d diskes:n,h); move(h,a,b,c); 从程序中可以看出,move函数是一个递归函数,它有四个形参n,x,y,z。n表示圆盘数,x,y,z分别表示三根针。move函数的功能是把x上的n个圆盘移动到z上。当n=1时,直接把x上的圆盘移至z上,输出xz。如n!=1则分为三步:递归调用move函数,把n-1个圆盘从x移到y;输出xz;递归调用move函数,把n-1个圆盘从y移到z。在递归调用过程中n=n-1,故n的值逐次递减,最后n=1时,终止递归,逐层返回。当n=4 时程序运行的结果为: input number: 4 the step
5、 to moving 4 diskes: ab ac bc ab ca cb ab ac bc ba ca bc ab acbc8.7 数组作为函数参数数组可以作为函数的参数使用,进行数据传送。数组用作函数参数有两种形式,一种是把数组元素(下标变量)作为实参使用;另一种是把数组名作为函数的形参和实参使用。1. 数组元素作函数实参数组元素就是下标变量,它与普通变量并无区别。 因此它作为函数实参使用与普通变量是完全相同的,在发生函数调用时,把作为实参的数组元素的值传送给形参,实现单向的值传送。例5.4说明了这种情况。【例8.7】判别一个整数数组中各元素的值,若大于0 则输出该值,若小于等于0则输出
6、0值。编程如下:void nzp(int v) if(v0) printf(%d ,v); else printf(%d ,0);main() int a5,i; printf(input 5 numbersn); for(i=0;i5;i+) scanf(%d,&ai); nzp(ai); 本程序中首先定义一个无返回值函数nzp,并说明其形参v为整型变量。在函数体中根据v值输出相应的结果。在main函数中用一个for语句输入数组各元素,每输入一个就以该元素作实参调用一次nzp函数,即把ai的值传送给形参v,供nzp函数使用。2. 数组名作为函数参数用数组名作函数参数与用数组元素作实参有几点不
7、同:1) 用数组元素作实参时,只要数组类型和函数的形参变量的类型一致,那么作为下标变量的数组元素的类型也和函数形参变量的类型是一致的。因此,并不要求函数的形参也是下标变量。换句话说,对数组元素的处理是按普通变量对待的。用数组名作函数参数时,则要求形参和相对应的实参都必须是类型相同的数组,都必须有明确的数组说明。当形参和实参二者不一致时,即会发生错误。2) 在普通变量或下标变量作函数参数时,形参变量和实参变量是由编译系统分配的两个不同的内存单元。在函数调用时发生的值传送是把实参变量的值赋予形参变量。在用数组名作函数参数时,不是进行值的传送,即不是把实参数组的每一个元素的值都赋予形参数组的各个元素
8、。因为实际上形参数组并不存在,编译系统不为形参数组分配内存。那么,数据的传送是如何实现的呢?在我们曾介绍过,数组名就是数组的首地址。因此在数组名作函数参数时所进行的传送只是地址的传送,也就是说把实参数组的首地址赋予形参数组名。形参数组名取得该首地址之后,也就等于有了实在的数组。实际上是形参数组和实参数组为同一数组,共同拥有一段内存空间。上图说明了这种情形。图中设a为实参数组,类型为整型。a占有以2000为首地址的一块内存区。b为形参数组名。当发生函数调用时,进行地址传送,把实参数组a的首地址传送给形参数组名b,于是b也取得该地址2000。于是a,b两数组共同占有以2000为首地址的一段连续内存
9、单元。从图中还可以看出a和b下标相同的元素实际上也占相同的两个内存单元(整型数组每个元素占二字节)。例如a0和b0都占用2000和2001单元,当然a0等于b0。类推则有ai等于bi。【例8.8】数组a中存放了一个学生5门课程的成绩,求平均成绩。float aver(float a5) int i; float av,s=a0; for(i=1;i5;i+) s=s+ai; av=s/5; return av;void main() float sco5,av; int i; printf(ninput 5 scores:n); for(i=0;i5;i+) scanf(%f,&scoi);
10、av=aver(sco); printf(average score is %5.2f,av);本程序首先定义了一个实型函数aver,有一个形参为实型数组a,长度为5。在函数aver中,把各元素值相加求出平均值,返回给主函数。主函数main 中首先完成数组sco的输入,然后以sco作为实参调用aver函数,函数返回值送av,最后输出av值。 从运行情况可以看出,程序实现了所要求的功能。3) 前面已经讨论过,在变量作函数参数时,所进行的值传送是单向的。即只能从实参传向形参,不能从形参传回实参。形参的初值和实参相同,而形参的值发生改变后,实参并不变化,两者的终值是不同的。而当用数组名作函数参数时,
11、情况则不同。由于实际上形参和实参为同一数组,因此当形参数组发生变化时,实参数组也随之变化。当然这种情况不能理解为发生了“双向”的值传递。但从实际情况来看,调用函数之后实参数组的值将由于形参数组值的变化而变化。为了说明这种情况,把例5.4改为例5.6的形式。【例8.9】题目同8.7例。改用数组名作函数参数。void nzp(int a5) int i; printf(nvalues of array a are:n); for(i=0;i5;i+) if(ai0) ai=0; printf(%d ,ai); main() int b5,i; printf(ninput 5 numbers:n);
12、 for(i=0;i5;i+) scanf(%d,&bi); printf(initial values of array b are:n); for(i=0;i5;i+) printf(%d ,bi); nzp(b); printf(nlast values of array b are:n); for(i=0;i5;i+) printf(%d ,bi);本程序中函数nzp的形参为整数组a,长度为5。主函数中实参数组b也为整型,长度也为5。在主函数中首先输入数组b的值,然后输出数组b的初始值。然后以数组名b为实参调用nzp函数。在nzp中,按要求把负值单元清0,并输出形参数组a的值。 返回主
13、函数之后,再次输出数组b的值。从运行结果可以看出,数组b的初值和终值是不同的,数组b的终值和数组a是相同的。这说明实参形参为同一数组,它们的值同时得以改变。用数组名作为函数参数时还应注意以下几点:a. 形参数组和实参数组的类型必须一致,否则将引起错误。b. 形参数组和实参数组的长度可以不相同,因为在调用时,只传送首地址而不检查形参数组的长度。当形参数组的长度与实参数组不一致时,虽不至于出现语法错误(编译能通过),但程序执行结果将与实际不符,这是应予以注意的。【例8.10】如把例8.9修改如下:void nzp(int a8) int i; printf(nvalues of array aar
14、e:n); for(i=0;i8;i+) if(ai0)ai=0; printf(%d ,ai); main() int b5,i; printf(ninput 5 numbers:n); for(i=0;i5;i+) scanf(%d,&bi); printf(initial values of array b are:n); for(i=0;i5;i+) printf(%d ,bi); nzp(b); printf(nlast values of array b are:n); for(i=0;i5;i+) printf(%d ,bi);本程序与例8.9程序比,nzp函数的形参数组长度改为
15、8,函数体中,for语句的循环条件也改为i8。因此,形参数组a和实参数组b的长度不一致。编译能够通过,但从结果看,数组a的元素a5,a6,a7显然是无意义的。c. 在函数形参表中,允许不给出形参数组的长度,或用一个变量来表示数组元素的个数。例如,可以写为:void nzp(int a)或写为void nzp(int a,int n)其中形参数组a没有给出长度,而由n值动态地表示数组的长度。n的值由主调函数的实参进行传送。由此,例8.10又可改为例8.11的形式。【例8.11】void nzp(int a,int n) int i; printf(nvalues of array a are:n
16、); for(i=0;in;i+) if(ai0) ai=0; printf(%d ,ai); main() int b5,i; printf(ninput 5 numbers:n); for(i=0;i5;i+) scanf(%d,&bi); printf(initial values of array b are:n); for(i=0;i5;i+) printf(%d ,bi); nzp(b,5); printf(nlast values of array b are:n); for(i=0;ib?a:b;return(c);main()int a=8;printf(%dn,max(a,
17、b);如果同一个源文件中,外部变量与局部变量同名,则在局部变量的作用范围内,外部变量被“屏蔽”,即它不起作用。8.9 变量的存储类别8.9.1 动态存储方式与静态动态存储方式前面已经介绍了,从变量的作用域(即从空间)角度来分,可以分为全局变量和局部变量。从另一个角度,从变量值存在的作时间(即生存期)角度来分,可以分为静态存储方式和动态存储方式。静态存储方式:是指在程序运行期间分配固定的存储空间的方式。动态存储方式:是在程序运行期间根据需要进行动态的分配存储空间的方式。用户存储空间可以分为三个部分:1) 程序区;2) 静态存储区;3) 动态存储区;全局变量全部存放在静态存储区,在程序开始执行时给
18、全局变量分配存储区,程序行完毕就释放。在程序执行过程中它们占据固定的存储单元,而不动态地进行分配和释放;动态存储区存放以下数据:1) 函数形式参数;2) 自动变量(未加static声明的局部变量);3) 函数调用实的现场保护和返回地址;对以上这些数据,在函数开始调用时分配动态存储空间,函数结束时释放这些空间。在c语言中,每个变量和函数有两个属性:数据类型和数据的存储类别。8.9.2 auto变量函数中的局部变量,如不专门声明为static存储类别,都是动态地分配存储空间的,数据存储在动态存储区中。函数中的形参和在函数中定义的变量(包括在复合语句中定义的变量),都属此类,在调用该函数时系统会给它
19、们分配存储空间,在函数调用结束时就自动释放这些存储空间。这类局部变量称为自动变量。自动变量用关键字auto作存储类别的声明。例如:int f(int a) /*定义f函数,a为参数*/auto int b,c=3; /*定义b,c自动变量*/a是形参,b,c是自动变量,对c赋初值3。执行完f函数后,自动释放a,b,c所占的存储单元。关键字auto可以省略,auto不写则隐含定为“自动存储类别”,属于动态存储方式。8.9.3 用static声明局部变量有时希望函数中的局部变量的值在函数调用结束后不消失而保留原值,这时就应该指定局部变量为“静态局部变量”,用关键字static进行声明。【例8.15
20、】考察静态局部变量的值。f(int a)auto b=0;static c=3;b=b+1;c=c+1;return(a+b+c);main()int a=2,i;for(i=0;i3;i+)printf(%d,f(a);对静态局部变量的说明:1) 静态局部变量属于静态存储类别,在静态存储区内分配存储单元。在程序整个运行期间都不释放。而自动变量(即动态局部变量)属于动态存储类别,占动态存储空间,函数调用结束后即释放。2) 静态局部变量在编译时赋初值,即只赋初值一次;而对自动变量赋初值是在函数调用时进行,每调用一次函数重新给一次初值,相当于执行一次赋值语句。3) 如果在定义局部变量时不赋初值的话
21、,则对静态局部变量来说,编译时自动赋初值0(对数值型变量)或空字符(对字符变量)。而对自动变量来说,如果不赋初值则它的值是一个不确定的值。【例8.16】打印1到5的阶乘值。int fac(int n)static int f=1;f=f*n;return(f);main()int i;for(i=1;i=5;i+)printf(%d!=%dn,i,fac(i);8.9.4 register变量为了提高效率,C语言允许将局部变量得值放在CPU中的寄存器中,这种变量叫“寄存器变量”,用关键字register作声明。【例8.17】使用寄存器变量。int fac(int n)register int i,f=1;for(i=1;i=n;i+)f=f*ireturn(f);main()int i;for(i=0;iy?x:y;return(z);main()
copyright@ 2008-2022 冰豆网网站版权所有
经营许可证编号:鄂ICP备2022015515号-1