1、数数 组组7.17.2 函函 数数7 7.1.1.1.1 数组的概念数组的概念 Add your text in here Click to add Text一组有序数据的集合称为数组。数组:数组:Add your text in here Click to add Text数组中的每一个数据称为一个数组元素。数组元素用数组名和下标来表示。数组元素:数组元素:Add your text in here Click to add Text数组名的命名规则与变量名的命名规则相同。数组名:数组名:Add your text in here Click to add Text用来表示数组元素在数组中的
2、排列顺序。TC规定,下标必须从0开始。下标:下标:Add your text in here Click to add Text表示一个数组元素所需要的下标的个数叫做表示一个数组元素所需要的下标的个数叫做 数组的维数。数组的维数。数组的维数数组的维数:7 7.1.1.2 2 一维数组一维数组1 1.一维数组的定义一维数组的定义v 格式格式:类型标识符 数组名 常量表达式;v 定义的目的定义的目的:为指定的数组分配相应字节的存储单元。int a 10;类型标识符类型标识符数组名数组名常量(大小)常量(大小)定义 a 为一维数组,可独立存放 10个整型数据,系统要为数组 a 分配 2*10=20
3、个字节的存储单元,且这些存储单元是连续的。7 7.1.1.2 2 一维数组一维数组 1)数组的类型实际上是指数组元素的取值类型。对于同一个数组,其所有元素的数据类型都是相同的。2)数组名的书写规则应符合标识符的书写规定。3)数组名不能与其它变量名相同。4)方括号中常量表达式表示数组元素的个数,如a5表示数组a有 5个元素。但是其下标从0开始计算。因此5个元素分别为0,a1,a2,a3,a4。对于数组类型说明应注意以下几点:7 7.1.1.2 2 一维数组一维数组 5)不能在方括号中用变量来表示元素的个数,但是可以是符号常数或常量表达式。6)允许在同一个类型说明中,说明多个数组和多个变量允许在同
4、一个类型说明中,说明多个数组和多个变量。2.2.一维数组元素的引用一维数组元素的引用 数组元素是组成数组的基本单元。数组元素也是一种变量,其标识方法为数组名后跟一个下标。下标表示了元素在数组中的顺序号。数组元素的一般形式为:数组元素的一般形式为:数组名数组名下标下标 其中下标只能为整型常量或整型表达式。如为小数时,C编译将自动取整。7 7.1.1.2 2 一维数组一维数组3.3.一维数组的初始化一维数组的初始化 给数组赋值的方法除了用赋值语句对数组元素逐个赋值外,还可采用初始化赋值和动态赋值的方法。数组初始化赋值是指在数组定义时给数组元素赋予初值。数组初始化是在编译阶段进行的。这样将减少运行时
5、间,提高效率。初始化值的一般形式为:初始化值的一般形式为:类型说明符类型说明符 数组名数组名 常量表达式常量表达式=值,值值,值值值;其中在 中的各数据值即为各元素的初值,各值之间用逗号间隔。7 7.1.1.2 2 一维数组一维数组1)可以只给部分元素赋值。当 中值的个数少于元素 个数时,只 给前面部分元 素赋值。1232)只能给元素逐个赋 值,不能给数组整 体赋值。3)如给全部元素赋 值,则在数组说 明中,可以不给 出数组元素的 数。7 7.1.1.2 2 一维数组一维数组4.4.一维数组的应用一维数组的应用main()int i,a10;for(i=0;i=0;i-)printf(%d,a
6、i);【例例】为一个一维数组赋值,然后按相反顺序输出。为一个一维数组赋值,然后按相反顺序输出。相似的例子相似的例子:从键盘上输入十个整:从键盘上输入十个整数放在一数组中,然后按相反顺序数放在一数组中,然后按相反顺序输出。输出。main()int i,a10;for(i=0;i=0;i-)printf(%d,ai);main()int a10;int i,j,t;printf(“Input 10 numbers:n);for(i=0;i10;i+)scanf(%d,&ai);printf(n);for(j=1;j=9;j+)for(i=0;iai+1)t=ai;ai=ai+1;ai+1=t;pr
7、intf(the sorted numbers:n);for(i=0;i10;i+)printf(%d ,ai);为数组元素赋值外循环控制比较轮数如果后一个元素比前一个元素小,二者交换输出排序后的数组内循环控制每轮比较次数相邻两个元素进行比较for(j=1;j=9;j+)for(i=0;iai+1)t=ai;ai=ai+1;ai+1=t;外循环变量保证执行n-1=9次,进行9轮比较内循环变量的初值是数组中最前边一个元素的下标内循环变量的终值的确定方法是:在第一轮比较时,i+1的最大值是数组中最后一个元素的下标,同时i应随j的增加而减少。即当j=1时,i的终值应是8,i+1应当为9main()i
8、nt a11;int i,j,t;printf(“Input 10 numbers:n);for(i=1;i11;i+)scanf(%d,&ai);printf(n);for(j=1;j=9;j+)for(i=1;iai+1)t=ai;ai=ai+1;ai+1=t;printf(the sorted numbers:n);for(i=1;i11;i+)printf(%d ,ai);数组元素a0未赋值,在整个程序中不使用该元素内循环变量的初值是数组中最前边一个元素的下标内循环变量的终值:当j=1时,i+1的最大值是数组中最后一个元素的下标,同时i应随j的增加而减少。即当j=1时,i的最大值应是9
9、,i+1应当为10外循环控制比较轮数7 7.1.1.3 3 二维数组二维数组1 1.二维数组的定义二维数组的定义v 格式:格式:类型标识符 数组名 常量1常量2;v 定义的目的定义的目的:为指定的数组分配相应字节的存储单元。int a 3 4;类型标识符类型标识符数组名数组名常量常量1 1,第一维的大小,第一维的大小常量常量2 2,第二维的大小,第二维的大小定义a为二维数组,可独立存放 3*4=12个整型数据,系统要为数组a分配2*12=24个字节的存储单元,且这些存储单元是连续的。7 7.1.1.3 3 二维数组二维数组2.2.二维数组的引用二维数组的引用 数组元素各维的下标都从0开始排列顺
10、序符合计数器规律。元素各维下标的最大值等于各维的大小减1。数组元素的下标可以为常量、变量、表达式数组元素的下标可以为常量、变量、表达式。二维数组可以看作是特殊的一维数组。格式格式:数组名数组名下标下标1 下标下标23.3.二维数组的初始化二维数组的初始化 二维数组的初始化就是在定义数组的同时对数组元素赋以初值。类型说明符类型说明符 数组名数组名 常量常量1常量常量2=数据表列数据表列 ;7 7.1.1.3 3 二维数组二维数组 int a34 =1,2,3,4,5,6,7,8,9,10,11,12 ;只能为常量只能为常量类型说明符类型说明符 数组名数组名 常量常量1常量常量2=数据表列数据表列
11、1,数据表列数据表列2,数据表列数据表列n ;int a34 =1,2,3,4,5,6,7,8,9,10,11,12 ;7 7.1.1.3 3 二维数组二维数组类型说明符类型说明符数组名数组名 常量常量1常量常量2=数据表列数据表列1,数据表列数据表列2,数据表列数据表列n ;int a34 =1,2,3,4,5,6,;类型说明符类型说明符 数组名数组名 常量常量2=数据表列数据表列1,数据表列数据表列2,数据表列数据表列n ;7 7.1.1.3 3 二维数组二维数组 int a 4 =1,2,3,4,5,6,;对全部行赋初值时,可以省略第一维的大对全部行赋初值时,可以省略第一维的大小,但第二
12、维的大小不能省略小,但第二维的大小不能省略。类型说明符类型说明符 数组名数组名 常量常量1常量常量2=数据表列数据表列 ;int a34 =2,3,;可以部分元素赋初值可以部分元素赋初值7 7.1.1.3 3 二维数组二维数组类型说明符类型说明符 数组名数组名 常量常量2=数据表列数据表列n;int a 4 =1,2,3,4,5,6,7,8,9,10,11,12 ;对全部数组元素赋初值时,可以不指定第一维的大小注意:注意:int a 3 =1,2,3,4,5,6;是绝对不是绝对不 允许的允许的第一维下标i 0,2 第二维下标j 0,3 循环嵌套实现数组元素的输入循环嵌套实现数组元素的输入循环嵌
13、套实现数组元素的输出循环嵌套实现数组元素的输出连续输出 4 个数组元素输出一个换行符号main()int i,j,a34;for(i=0;i=2;i+)for(j=0;j=3;j+)a i j=i*j;for(i=0;i=2;i+)for(j=0;j=3;j+)printf(%d ,a i j );printf(n);7 7.1.1.3 3 二维数组二维数组4.4.二维数组的应用二维数组的应用【例例】在程序中为一个二维数组赋值,然后输出在程序中为一个二维数组赋值,然后输出。一维数组的应用一维数组的应用7 7.1.1.3 3 二维数组二维数组main()int a34=1,2,3,4 ,9,8,
14、7,6 ,-10,10,-5,2 ,i,j;int max,row,cloum;max=a 0 0;max=a 0 0;row=0;colum=0;row=0;colum=0;printf(max=%d,row=%d,colum=%dn,max,row,colum);/*假定a 0 0 为最大值*/for(i=0;i3;i+)for(j=0;j4;j+)if(maxa i j )max=a i j ;row=i;colum=j;/*记下a 0 0 的行和列下标 */*max(最大值),row(行下标)和colun(列下标)*/*记下新的最大值和下标记下新的最大值和下标*/【例例】求二维数组中最
15、大元素及其下标求二维数组中最大元素及其下标。7 7.1.1.4 4 字符数组字符数组213字符数组用来存放字符型数据的数组叫作字符数组,字符数组中的每个元素存放一个字符C 语言中,字符串都是以字符数组的形式存储的字符数组也分成一维字符数组,二维字符数组,多维字符数组1 1.字符数组的定义字符数组的定义7 7.1.1.4 4 字符数组字符数组2.2.字符数组的初始化字符数组的初始化1 1)一)一维数组的初始化维数组的初始化格式格式1:1:类型说明符类型说明符 数组名数组名 常量常量 =字符常量表列字符常量表列 ;例如:例如:char a10=c,p,r,o,g,r,a,m;初始化赋值后各元素的值
16、为:初始化赋值后各元素的值为:cprograma0 a1 a2 a3 a4 a5 a6 a7 a8 a97 7.1.1.4 4 字符数组字符数组格式格式2:2:类型说明符类型说明符 数组名数组名=字符常量表列字符常量表列 ;例如:例如:char a=c,p,r,o,g,r,a,m;初始化赋值后各元素的值为:初始化赋值后各元素的值为:cprograma0 a1 a2 a3 a4 a5 a6 a7 a8当对全体元素赋初值时也可以省去长度说明。这时a数组的长度自动定为9。7 7.1.1.4 4 字符数组字符数组格式格式3:3:类型说明符类型说明符 数组名数组名 常量常量 =字符串常量字符串常量 ;例
17、如:例如:char c12=abcd1234567 ;或者或者 char c12=abcd1234567 ;将双引号中的字符依次赋给各个数组元素,即数组元素c0,c1,c10,c11 的值分别为:a,b,5,6,7,0。7 7.1.1.4 4 字符数组字符数组2 2)二)二维数组的初始化维数组的初始化格式格式1:1:类型说明符类型说明符 数组名数组名 常量常量1 1 常量常量2 2=字符常量表列字符常量表列 ;例如:例如:char a3 4 =1,2,3,4,A,B,C,D,a,b,c,d ;将花括号中的常量依次赋给各个元素,即数组元素a00,a01,a22,a23 的值分别为:1,2,3,4
18、,A,B,C,D,a,b,c,d。但要注意各行中均没有字符串结束的标志0。7 7.1.1.4 4 字符数组字符数组格式格式2:2:类型说明符类型说明符 数组名数组名 常量常量1 1 常量常量2 2=字符常量表列字符常量表列1 1,字符常量表字符常量表2 2,字符常量表字符常量表n n ;例如:例如:char a3 4 =1,2,3,4,A,B,C,D,a,b,c,d ;第一个花括号中的常量依次赋给第一行的各个元素,将第二个花括号中的常量依次赋给第二行的各个元素,即数组元素a00,a01,a22,a23的值分别为:1,2,3,4,A,B,C,D,a,b,c,d。同样各行中均无字符串结束的标志0。
19、7 7.1.1.4 4 字符数组字符数组格式格式3:3:类型说明符类型说明符 数组名数组名 常量常量1 1 常量常量2 2=字符串字符串1 1 ,字符串字符串2 2,字符串字符串n n ;将第一个花括号中的各个字符依次赋给第一行的各个元素,将第二个花括号中的各个字符依次赋给第二行的各个元素,即数组元素:a00,a01,a22,a23的值分别为:1,2,3,4,A,B,C,D,a,b,c,d。但每行的最后都有字符串结束标志0。例如:例如:static char a 3 5 =1234,ABCD,abcd ;第二维大小为57 7.1.1.4 4 字符数组字符数组格式格式4:4:类型说明符类型说明符
20、 数组名数组名 常量常量1 1 常量常量2 2=字符串字符串1 1,字符串字符串2 2,字符串字符串n n ;将第一个字符串常量中的字符依次赋给第一行的各个元素,将第二个字符串常量中的字符依次赋给第二行的各个元素,即数组元素a00,a01,a22,a23的值分别为 1,2,3,4,A,B,C,D,a,b,c,d。但每行的最后都有字符串结束标志0。例如:例如:static char a 3 5 =1234,ABCD,abcd ;第二维大小为57 7.1.1.4 4 字符数组字符数组3.3.字符数组的引用字符数组的引用【例例】main()int i,j;char a 5=B,A,S,I,C,d,B
21、,A,S,E;for(i=0;i=1;i+)for(j=0;j=4;j+)printf(%c,aij);printf(n);7 7.1.1.4 4 字符数组字符数组4.4.字符串和字符串结束标志字符串和字符串结束标志字符串常量:字符串常量:两个双引号之间的字符序列叫做字符串常量,如abcdefg。字符串结束标志:字符串结束标志:系统自动为每一个字符串常量加一个字符串结束标志0,在内存中也占一个字节。字符串常量所占的存储单字符串常量所占的存储单元字节数:元字节数:字符 串中有效字符的个数加1,因为字符串结束的标志0也占一个字节。在TC中,系统是将一个字符串常量按一维字符数组处理的。7 7.1.1
22、.4 4 字符数组字符数组5.5.字符数组的输入输出字符数组的输入输出1 1)单个)单个字符输入字符输入 在 scanf 函数中使用格式字符%c输入单个字符:scanf(%c,&c0);l 使用getchar()函数:c1=getchar();l 使用赋值语句输入单个字符:c1=A;2 2)初始化)初始化时整体输入时整体输入char c 20=abcdef123?&;将各个字符按顺序赋给各个数组元素,同时将字符串结束的标志0也赋给数组元素。即a11中的值为0。7 7.1.1.4 4 字符数组字符数组6.6.字符串处理函数字符串处理函数3 3)用)用 scanf 函数整体输入函数整体输入char
23、 c10;scanf(%s,c);只使用数组名不加取地址符号 从键盘上读入一个字符串,所读入的字符串中不能包含空格、TAB和回车。将串中的各个字符按顺序赋给各个数组元素,在最后一个字符的后面自动加上字符串结束的标志0也赋给数组元素。在 C 的库函数中提供了一些用来处理字符串的函数,这些函数对于字符串的处理使用非常方便,常用的字符串处理函数是:7 7.1.1.4 4 字符数组字符数组字符串处理字符串处理函数函数字符串输出函数:puts()字符串输入函数:gets()字符串连接函数:strcat()字符串复制函数:strcpy()字符串比较函数:strcmp()字符串测长函数:strlen()字符
24、串大小写转换函数 strlwr()和 strupr()注意:注意:用于输入输出的字符串函数,在使用前应包含头文件stdio.h,使用其它字符串函数则应包含头文件string.h7 7.1.1.4 4 字符数组字符数组6.6.字符数组程序举例字符数组程序举例【例例】将两个字符串将两个字符串s1s1和和s2s2连接起来连接起来。include“stdio.h”include“string.h”main()char s110,s210;gets(s1);gets(s2);printf(String1 is:%sn,s1);printf(String2 is:%sn,s2);strcat(s1,s2)
25、;printf(Result is:%sn,s1);方法一:使用strcat函数7 7.1.1.4 4 字符数组字符数组方法二:不使用方法二:不使用strcatstrcat函数函数main()char s180,s280;int i,j;gets(s1);gets(s2);for(i=0;s1i!=0;i+);for(j=0;s2j!=0;i+,j+)s1i=s2j ;s1i=0;puts(s1);如果从键盘上输入的两行字符串分别为:12345 和 abcd,则程序运行的过程为:i:9123450?abcd0?j:4str1:str2:0abcd关于程序和函数的说明关于程序和函数的说明C语言的
26、程序是由一个或多个函数组成的。一个源程序文件是一个编译单位。C程序执行是从main函数开始,在main函数中结束,不管main函数的位置如何。所有的函数独立定义,互相调用。main()函数可以调用任意其它函数,其它函数之间可以相互调用,但任何函数都不能调用main()函数。关于关于数学函数和数学函数和C语言中函数的对比语言中函数的对比说明说明调用函数调用函数实际参数实际参数函数值函数值f(0)=f(1)=f(2)=0*20*321*21*322*22*32=0=2.23607=4如数学中有:如数学中有:f(x)=定义函数定义函数形式参数形式参数xx232再如:再如:g(x,y)=g(3,4)=
27、5定义函数定义函数调用函数调用函数两个形式参数两个形式参数两个实际参数两个实际参数函数值函数值22yx2243 函数的分类函数的分类按照使用特按照使用特点点标准函数(库函数)标准函数(库函数):由系统提供,解决公共问题,使用时要包含相应的库文件自定义函数自定义函数:由用户自己定义,解决特定的问题有参函数有参函数:主调函数与被调函数之间有参数传递无参函数:主调函数与被调函数之间没有参数传递 通常来执行一组操作按照函数形按照函数形式式7 7.2 2.1 1 函数的定义函数的定义 函数必须定义才能够调用,在函数定义时,要对函数的类型、形参及其类型、函数中所要完成的操作、返回的函数值进行一系列的规定,
28、根据这些规定才能够正确的对函数进行调用。其中函数中所完成的操作是函数的核心部分,是由程序语句实现的;函数及形参的类型是正确使用函数的依据;函数返回的值是调用函数后所得到的结果。1.1.函数的定义形式函数的定义形式类型标识符类型标识符 函数名函数名()()声明部分声明部分 语句语句 其中类型标识符和函数名称为函数头。类型标识符指明了本函数的类型,函数的类型实际上是函数返回值的类型。函数名是由用户定义的标识符,函数名后有一个空括号,其中无参数,但括号不可少。7 7.2 2.1 1 函数的定义函数的定义 中的内容称为函数体。在函数体中声明部分,是对函数体内部所用到的变量的类型说明。在很多情况下都不要
29、求无参函数有返回值,此时函数类型符可以写为 void。2.2.有参函数的定义形式有参函数的定义形式类型标识符类型标识符 函数名函数名(形式参数表列形式参数表列)声明部分声明部分 语句语句 7 7.2 2.1 1 函数的定义函数的定义3.3.空函数空函数类型标识符类型标识符 函数名函数名()()在程序中调用此函数时,什么也不做,没有任何实际作用。在程序中使用空函数是为了便于将来对程序进行模块扩展。有参函数比无参函数多了一个内容,即形式参数表列。在形参表中给出的参数称为形式参数,简称形参,它们可以是各种类型的变量,各参数之间用逗号间隔。在进行函数调用时,主调函数将赋予这些形式参数实际的值。形参既然
30、是变量,必须在形参表中给出形参的类型说明。7 7.2 2.2 2 函数参数和函数的值函数参数和函数的值1.1.形参和实参形参和实参 形参出现在函数定义中,在整个函数体内都可以使用,离开该函数则不能使用。实参出现在主调函数中,进入被调函数后,实参变量也不能使用。形参和实参的功能是作数据传送。发生函数调用时,主调函数把实参的值传送给被调函数的形参从而实现主调函数向被调函数的数据传送。函数的形参和实参具有以下特点:1)形参变量只有在被调用时才分配内存单元,在调用结束时,即刻释放所分配的内存单元。因此,形参只有在函数内部有效。函数调用结束返回主调函数后则不能再使用该形参变量。7 7.2 2.2 2 函
31、数参数和函数的值函数参数和函数的值 2)实参可以是常量、变量、表达式、函数等,无论实参是何种类型的量,在进行函数调用时,它们都必须具有确定的值,以便把这些值传送给形参。因此应预先用赋值,输入等办法使实参获得确定值。3)实参和形参在数量上,类型上,顺序上应严格一致,否则会发生类型不匹配”的错误。4)函数调用中发生的数据传送是单向的。即只能把实参的值传送给形参,而不能把形参的值反向地传送给实参。因此在函数调用过程中,形参的值发生改变,而实参中的值不会变化。7 7.2 2.2 2 函数参数和函数的值函数参数和函数的值2.2.函数的返回值函数的返回值 函数的值是指函数被调用之后,执行函数体中的程序段所
32、取得的并返回给主调函数的值。对函数的值(或称函数返回值)有以下一些说明:函数的值只能通过return语句返回主调函数。return 语句的一般形式为:return 表达式;表达式;或者为:return(表达式表达式);该语句的功能是计算表达式的值,并返回给主调函数。在函数中允许有多个return语句,但每次调用只能有一个return 语句被执行,因此只能返回一个函数值。7 7.2 2.2 2 函数参数和函数的值函数参数和函数的值 函数值的类型和函数定义中函数的类型应保持一致。如果两者不一致,则以函数类型为准,自动进行类型转换。如函数值为整型,在函数定义时可以省去类型说明。不返回函数值的函数,可
33、以明确定义为“空类型”,类型说明符为“void”。一旦函数被定义为空类型后,就不能在主调函数中使用被调函数的函数值了。7 7.2 2.3 3 函数的调用函数的调用1.1.函数调用的一般形式函数调用的一般形式格式:格式:函数名函数名(实参表列实参表列)1.对无参函数,实参表列可以省略,但括 号不能省略2.形参与实参按顺序一一对应 函数的调用实际上就是将实参的值传递给相应的形参,然后转到函数中去执行各个语句,如果函数中有return语句,则返回一个函数值。说说明明在函数调用中还应该在函数调用中还应该注意的一个问题是求注意的一个问题是求值顺序的问题值顺序的问题。7 7.2 2.3 3 函数的调用函数
34、的调用把函数调用作为一个语句,如 printf()函数,这种调用方式不使用函数的返回值,因而在函数体中不需要return语句。.把函数调用作为表达式的一部分,这种调用方式使用函数返回的函数值,因而在函数体中一定要使用return语句返回一个确定的函数值。函数参数:把函数调用作为一个实参进行函数调用,这种调用方式也要使用函数的返回函数值,因而在函数体中也一定要使用return语句返回确定的函数值。2.2.函数调用的方式函数调用的方式7 7.2 2.3 3 函数的调用函数的调用3.3.函数调用的方式函数调用的方式 在主调函数中调用某函数之前应对该被调函数进行说明(声明),这与使用变量之前要先进行变
35、量说明是一样的。在主调函数中对被调函数作说明的目的是使编译系统知道被调函数返回值的类型,以便在主调函数中按此种类型对返回值作相应的处理。其一般形式为:其一般形式为:类型说明符类型说明符 被调函数名被调函数名(类型类型 形参,类型形参,类型 形参形参);或为:或为:类型说明符类型说明符 被调函数名被调函数名(类型,类型类型,类型);如果被调函数的返回值是整型或字符型时,可以不对被调函数作说明,而直接调用。这时系统将自动对被调函数返回值按整型处理。7 7.2 2.3 3 函数的调用函数的调用 语言中又规定在以下几种情况时可以省去主调函数中对被调函数的函数说明:当被调函数的函数定义出现在主调函数之前
36、时,在主调函数中也可以不对被调函数再作说明而直接调用。对库函数的调用不需要再作说明,但必须把该函数的头文件用include命令包含在源文件前部。7 7.2 2.3 3 函数的调用函数的调用 如在所有函数定义之前,在函数外预先说明了各个函数的类型,则在以后的各主调函数中,可不再对被调函数作说明。7 7.2 2.3 3 函数的调用函数的调用嵌套调用:嵌套调用:在调用一个函数的过程中又调用了另外一个函数,叫作函数的嵌套调用。嵌套调用的执行过程嵌套调用的执行过程:从什么地方调用函数,就返回到什么地方。7 7.2 2.3 3 函数的调用函数的调用 .调用函数调用函数fun2main()函数函数 .调用函
37、数调用函数fun1fun1()函数函数fun2()函数函数结束结束main()函数中调用了函数fun1,函数fun1中调用了函数fun27 7.2 2.4 4 函数的递归调用函数的递归调用 递归调用递归调用:在调用一个函数的过程中又直接或间接地调用该函数本身,叫作函数的递归调用。递归调用应注意的问题递归调用应注意的问题:要防止无限次的递归调用。防止无限递归调用的方法防止无限递归调用的方法:使用 if 语句设置一个递归过程结束的条件,且在递归过程中使该条件逐步趋于成立。由此可见:递归就是把有同样操作的运算的规模一次一次的减小,直到可解 为止。递归递推归溯递归递推归溯7 7.2 2.4 4 函数的
38、递归调用函数的递归调用1.1.数组作为函数参数数组作为函数参数 数组可以作为函数的参数使用,进行数据传送。数组用作函数参数有两种形式:一种是一种是 把数组元素作为实参使用,这时候是把数组元素的值传给形参使用。另一种是另一种是 是把数组名作为函数的形参和实参使用,这时候传送的是数组的首地址。数组的首地址就是数组诸元素在内存中所占存储单元中最前边一个单元的编号。7 7.2 2.4 4 函数的递归调用函数的递归调用1 1)数组)数组元素作函数实参元素作函数实参 数组元素又称下标变量,它与普通变量并无区别。因此它作为函数实参使用与普通变量是完全相同的,在发生函数调用时,把作为实参的数组元素的值传送给形
39、参,实现单向的值传送。2 2)数组)数组名作函数实参名作函数实参 (1)用数组元素作实参时,只要数组类型和函数的形参变量的类型一致,那么作为下标变量的数组元素的类型也和函数形参变量的类型是一致的。因此,并不要求函数的形参也是下标变量。(2)用数组名作函数参数时,则要求形参和相对应的实参都必须是类型相同的数组,都必须有明确的数组说明。7 7.2 2.4 4 函数的递归调用函数的递归调用 (3)在数组名作函数参数时所进行的传送只是地址的传送,也就是说把实参数组的首地址赋予形参数组名。形参数组名取得该首地址之后,也就等于有了实在的数组。实际上是形参数组和实参数组为同一数组,共同拥有一段内存空间。(4
40、)形参的初值和实参相同,而形参的值发生改变后,实参并不变化,两者的终值是不同的。而当用数组名作函数参数时,情况则不同。由于实际上形参和实参为同一数组,因此当形参数组发生变化时,实参数组也随之变化。当然这种情况不能理解为发生了“双向”的值传递。但从实际情况来看,调用函数之后实参数组的值将由于形参数组值的变化而变化。7 7.2 2.4 4 函数的递归调用函数的递归调用用数组名作为函数参数时还应注意以下几点:(1)形参数组和实参数组的类型必须一致,否则将引起错误。(2)形参数组和实参数组的长度可以不相同,因为在调用时,只传送首地址而不检查形参数组的长度。当形参数组的长度与实参数组不一致时,虽不至于出
41、现语法错误(编译能通过),但程序执行结果将与实际不符,这是应予以注意的。7 7.2 2.5 5 局部变量和全局变量局部变量和全局变量1.1.局部变量局部变量 局部变量也称为内部变量。局部变量是在函数内作定义说明的。其作用域仅限于函数内,离开该函数后再使用这种变量是非法的。关于局部变量的作用域还要说明以下几点:主函数中定义的变量也只能在主函数中使用,不能在其它函数中使用。同时,主函数中也不能使用其它函数中定义的变量。因为主函数也是一个函数,它与其它函数是平行关系。这一点是与其它语言不同的,应予以注意。7 7.2 2.5 5 局部变量和全局变量局部变量和全局变量 形参变量是属于被调函数的局部变量,
42、实参变量是属于主调函数的局部变量。允许在不同的函数中使用相同的变量名,它们代表不同的对象,分配不同的单元,互不干扰,也不会发生混淆。如在前例中,形参和实参的变量名都为n,是完全允许的。在复合语句中也可定义变量,其作用域只在复合语句范围内。7 7.2 2.5 5 局部变量和全局变量局部变量和全局变量Add Your Text2.2.全局变量全局变量 全局变量也称为外部变量,它是在函数外部定义的变量。它不属于哪一个函数,它属于一个源程序文件。其作用域是整个源程序。在函数中使用全局变量,一般应作全局变量说明。只有在函数内经过说明的全局变量才能使用。全局变量的说明符为 extern。但在一个函数之前定
43、义的全局变量,在该函数内使用可不再加以说明。3.3.变量的储存类别变量的储存类别1 1)动态动态存储方式与静态存储方式存储方式与静态存储方式 变量的存储类别是按变量值存在的时间(即生存期)来进行分类的,分为两类:动态存储方式与静态存储方式。按存储类别,变量可分为动态存储变量和静态存储变量。7 7.2 2.5 5 局部变量和全局变量局部变量和全局变量内存内存系统区系统区用户区用户区存放系统程序存放用户文件和数据存放用户文件和数据程序区:静态存储区:静态存储区:动态存储区:程序全局变量全局变量静态局部变量静态局部变量(用(用static声明的变量)声明的变量)局部自动变量(auto)函数的形参7
44、7.2 2.5 5 局部变量和全局变量局部变量和全局变量动态存储动态存储调用函数时分配存储单元,函数调用结束时释放所占存储单元。即离开函数后变量的值不能被引用,变量的生存期是函数运行期间。自动局部变量、函数的形参以及函数调用时的现场保护和返回地址。动态存储区中存储:动态存储区中存储:静态存储静态存储定义变量时分配存储单元,函数调用结束时并不释放所占存储单元。离开函数后变量的值仍能被引用,变量的生存期是程序运行期间。静态存储区中存储:静态存储区中存储:静态局部变量、全局变量。7 7.2 2.5 5 局部变量和全局变量局部变量和全局变量 在C语言中,每一个变量都有两种属性:数据类型和存储类别,前者
45、表示了变量所能存放的数据类型和范围,后者表示了变量在内存中的存储方式。2 2)auto 变量变量 函数中的形参和在函数中定义的变量(包括在复合语句中定义的变量),都属此类,在调用该函数时系统会给它们分配存储空间,在函数调用结束时就自动释放这些存储空间。这类局部变量称为自动变量。自动变量用关键字auto作存储类别的声明。3 3)static 变量变量 有时希望函数中的局部变量的值在函数调用结束后不消失而保留原值,这时就应该指定局部变量为“静态局部变量”,用关键字static进行声明。7 7.2 2.5 5 局部变量和全局变量局部变量和全局变量 对静态局部变量的说明:1)静态局部变量属于静态存储类
46、别,在静态存储区内分配存储单元。在程序整个运行期间都不释放。而自动变量(即动态局部变量)属于动态存储类别,占动态存储空间,函数调用结束后即释放。2)静态局部变量在编译时赋初值,即只赋初值一次;而对自动变量赋初值是在函数调用时进行,每调用一次函数重新给一次初值,相当于执行一次赋值语句。7 7.2 2.5 5 局部变量和全局变量局部变量和全局变量3)如果在定义局部变量时不赋初值的话,则对静态局部变量来说,编译时自动赋初值0(对数值型变量)或空字符(对字符变量)。而对自动变量来说,如果不赋初值则它的值是一个不确定的值。4)使用静态存储要多占用内存空间,而且降低了程序的可读性,因此,除非有必要,否则不
47、要多用静态局部变量。7 7.2 2.5 5 局部变量和全局变量局部变量和全局变量4 4)resgister 变量变量 为了提高效率,C 语言允许将局部变量的值放在 CPU 中的寄存器中,这种变量叫“寄存器变量”,用关键字 register 作声明。.1)只有局部自动变量和形式参数可以作为寄存器变量。3)局部静态变量不能定义为寄存器变量。2)一个计算机系统中的寄存器数目有限,不能定义任意多个寄存器变量。7 7.2 2.5 5 局部变量和全局变量局部变量和全局变量5 5)用)用 extern 声明外部变量声明外部变量 外部变量(即全局变量)是在函数的外部定义的,它的作用域为从变量定义处开始,到本程
48、序文件的末尾。如果外部变量不在文件的开头定义,其有效的作用范围只限于定义处到文件终了。如果在定义点之前的函数想引用该外部变量,则应该在引用之前用关键字 extern 对该变量作“外部变量声明”。表示该变量是一个已经定义的外部变量。有了此声明,就可以从“声明”处起,合法地使用该外部变量。说明说明:在本程序文件的最后1行定义了外部变量A,B,但由于外部变量定义的位置在函数 main 之后,因此本来在 main 函数中不能引用外部变量 A,B。现在我们在 main 函数中用 extern 对 A 和 B进行“外部变量声明”,就可以从“声明”处起,合法地使用该外部变量 A 和 B。7 7.2 2.5
49、5 局部变量和全局变量局部变量和全局变量按作用域局部变量自动变量:即动态局部变量静态局部变量寄存器变量(形式参数可以定义为自动变量或者寄存器变量)全局变量静态外部变量外部变量:即非静态外部变量7 7.2 2.5 5 局部变量和全局变量局部变量和全局变量按生存期动态存储自动变量寄存器变量形式参数静态存储静态外部变量外部变量:即非静态外部变量按变量值存放位置内存中动态存储区自动变量形式参数静态局部变量外部变量:即非静态外部变量静态外部变量内存中静态存储区CPU中的寄存器:寄存器变量7 7.2 2.5 5 局部变量和全局变量局部变量和全局变量7 7.2 2.5 5 局部变量和全局变量局部变量和全局变量 要注意函数参数传递的两种形式:一种是单向的值传递形式,这种情况下实参可以是常量,变量,表达式,数组元素,形参是变量;另一种是地址传递形式,这种情况下实参是数组名或者数组名和数组的长度,形参是数组或者数组和数组的长度。2 要注意函数类型和返回值类型,实参类型和形参类型的一致性。3 1要注意有参函数和无参函数、有返回值函数和无返回值函数的区别与应用。7 7.2 2.5 5 局部变量和全局变量局部变量和全局变量要注意全局变量和局部变量,静态存储变量和动态存储变量在程序设计中的应用。54要学会用一些简单的递归调用来解决用常规方法不容易解决的问题。