C语言程序设计实用教程第6章.ppt
第6章 数组,教学提示 数组是由基本数据类型构造出来的、有固定大小和相同类型的变量的集合。通过数组可以对一批具有相同数据类型的数据进行处理。教学目标 掌握一维数组、二维数组和字符数组的定义、初始化及基本操作,掌握数组元素的引用、赋值、输入和输出,并能运用数组解决数值和非数值数据处理中的典型问题。,6.1数组的概念,在实际应用中,有时要处理的数据量可能很大,例如要对几千名学生的成绩从高到低排序。对于这种需要处理大批相同类型数据的情况,在程序设计中最有效的办法就是使用数组。数组和循环相结合,编写出的程序魅力无穷。,6.1.1 引例,例6.1 求一个班100个学生的平均成绩,并统计成绩高于平均分的学生人数。,分析:求平均成绩,可用简单变量和循环结构相结合来完成,程序段如下:,float score,sum=0;for(i=1;i=100;i+)printf(“please input grade of No%d:”,i);scanf(“%f”,/*求出平均成绩*/,在本程序中能否统计高于平均分的学生人数呢?,用数组来解决这个问题,不仅效率高而且程序编写容易。#define N 100 void main()int i,n=0;float scoreN,sum=0,aver;printf(“please input students grade:”);for(i=0;iaver)n+;printf(“average=%.2f n”,aver);printf(“n=%d”,n);,scoreN表示数组,用来存放N个学生的成绩,scorei是数组元素,用来存放第i个学生成绩*,6.1.2 数组中的相关概念,1数组:是指一组具有相同类型的数据的有序的集合。2数组下标:是数组元素在数组中位置的一个索引或指示。3数组元素:数组中的元素。4数组大小:数组中元素的个数,也称数组长度。5数组的维数:数组元素下标的个数。根据数组的维数可以将数组分为一维、二维、三维、多维数组。,62 一维数组,只有一个下标的数组称为一维数组。一维数组通常和一重循环相配合,对数组元素进行处理。,6.2.1 一维数组的定义定义数组即指明数组名、数组类型和数组大小,定义一维数组的格式为:类型标识符 数组名整型常量表达式;,例如:int a10;定义了一个一维数组a,该数组有10个元素,其中每个数组元素都是整型数据。,一维数组定义说明:1.定义格式:类型标识符 数组名整型常量表达式;2.例:int a10;,其中:1、类型标识符表示数组中元素的数据类型。2、数组名命名规则必须遵循C语言中的标识符命名规则。3、整形常量表达式表示数组长度,可以包含常量和符号常量,但不能使用变量,例如下面的定义是不对的:int n=10;int bn;,6.2.2 一维数组元素的引用,数组元素的引用是用数组名加下标组成的。一维数组元素的引用形式为:数组名下标,需要注意的是:C语言中数组的下标是从0开始的。,例如若定义了一个数组:int a5;则数组a 中的5个元素依次是:a0,a1,a4注意:下标从0到4,数组中并没有a5这个元素。,说明:1.引用数组元素时下标可以是整型变量、整形常量或整形表达式;2.数组元素本身可以看作是同一个类型的单个变量;3.C编译系统不会对数组下标越界进行检查。错误地引用越界数组元素可能会破坏数组后的其他数据,造成不可预料的后果。因此引用数组元素时,要注意避免下标越界。,例6.2 写出下面程序的运行结果。main()int a10,i;for(i=0;i10;i+)ai=i+1;printf(“%3d”,ai);,程序运行时输出:1 2 3 4 5 6 7 8 9 10,6.2.3 一维数组的初始化,所谓数组的初始化,就是在定义数组的同时就给数组元素指定初值。,数组元素的值可以通过以下方式获得:1、通过赋值语句或输入语句得到,如:ai=i+1;scanf(“%d”,但两者是有区别的,前者在程序运行时进行,占用运行时间;后者则是在程序运行前,即编译阶段就给数组元素赋值,不占用程序运行时间。,一维数组的初始化有以下几种情形:,1、初始化全部数组元素。例如:int a5=1,2,3,4,5;或:int a=1,2,3,4,5;/*此时可以省略数组大小*/,2、初始化部分元素。例如:static int a5=1,2;数组大小为5,但只有2个初值,这时只给前2个元素赋值,后面的3个元素的值都为0。即:a0=1,a1=2,a2、a3、a4均为0,这时数组长度不能省略。关键字“static”表示将数组定义成静态存储。,3将数组的所有元素初始化为0。例如:static int a5=0;则数组a中元素值均为0。,注意:,(1)如果只是定义数组,而不对其进行初始化,例如:int a5;则数组a中元素的初始值为系统分配给数组元素的内存单元中的原始值,这些值对编程者来说是不可预知的,因此在使用时要注意。(2)若定义数组时在类型名前面加一个关键字static,即将数组定义成静态存储,这时不为数组赋初值,系统也会自动将全部元素赋以0值。例如:static int b5;表示定义了一个静态数组b,该数组中所有元素值均为0。,6.2.4 一维数组的存储结构,在定义一个数组后,系统会在内存中分配一段连续的存储空间,用来存放数组中的元素。一维数组中的元素按下标从小到大的顺序存放在内存中,每个元素占用的空间大小相同。,6.2.4 一维数组的存储结构,例如若有定义:int a5;则数组a 在内存中的存储形式如图6.1所示。,图6.1 一维数组在内存中的存放结构,C语言规定:数组名代表数组在内存中的起始地址。根据数组起始地址和数组元素的下标,可以计算出数组中任一个元素的存储地址。计算公式如下:数组元素地址=数组起始地址+元素下标*sizeof(数组类型)例如,假设数组a的起始地址为1000,则a3的地址为:&a3=a+3*sizeof(int)=1000+3*2=1006,6.2.5 一维数组的典型实例,例6.3 一维数组的输入与输出。输入输出是数组的基本操作,但整个数组的值并不能作为一个整体一次输入或输出,而只能是逐个元素地处理。一维数组的输入、输出往往都是用一重循环来实现的。下面是一个完整的程序:,程序运行效果如下:please input array a(4 numbers):5 8 12-9output array a:a0=5 a1=8 a2=12 a3=-9,#include main()int i,a4;printf(“nplease input array a(4 numbers):”);for(i=0;i4;i+)/*注意下标范围,不要越界*/scanf(“%d”,例6.4 求一维数组元素中的最大值及其所在下标。求最值及其位置是数组中一种常用的算法。,比如有一个存储5个学生成绩的数组:int cj=80,67,76,87,78;若想找出其中的最高分,我们可一下看出结果:cj3=87 但如果不是5个成绩,而是500个成绩呢?我们就不能一眼看出了,而要不断地从一个个成绩里搜寻那个最大值。怎么在数组中找最大或最小值呢?,其实,求最值是一个重复“比较”的过程。,下面以5个数为例,看看如何找出5个数中的最大值:2、3、1、4、0,用 max 来表示最大值,loca表示其所在下标。1、首先假设第一个数就是最大值,即max=2,loca=0;2、把max和第二个数比较,3 比 max大,于是 max=3,loca=1;3、把max和第三个数比较,1 不比max大,max和loca都不变;4、把max和第四个数比较,4 比 max 大,于是 max=4,loca=3;5、把max和第五个数比较,0 不比max大,于是max和loca都不变;最后,max=4,loca=3。,下面给出一个完整的程序:,规纳起来,对于n个数,要求其中的最大值,基本思路是这样的:1、假设第一个数就是最大值max;2、把max和下一个数比较,如果下一个数比max大,则将该数赋给max;3、重复第二步,直到比较到最后一个数为止。,#include main()int a5=80,67,76,87,78;int i,max,loca;max=a0;loca=0;for(i=1;i5;i+)if(maxai)max=ai;loca=i;printf(“max=a%d=%dn”,loca,max);,假设第一个元素就是最大值,loca保存其下标,程序运行结果如下:max=a3=87,例6.5 冒泡排序。,排序是数组的一个基本操作,即将数组中的元素按升序或降序排列。“冒泡排序”是一种典型的排序方法,以升序为例,冒泡排序的基本思想是:1、从前向后,依次比较相邻的两个元素,如果后面的比前面小,就将二者交换。如此反复比较,直到最后两个元素。此时,最大值已换到了最末位置,即最后一个元素已排好。2、对剩余元素重复第一步,直至将所有元素排好为止。,#include#define N 10main()int aN,i,j,t;printf(“please input array a:”);for(i=0;iaj+1)t=aj;aj=aj+1;aj+1=t;printf(“after sorted:”);for(i=0;i10;i+)printf(“%d”,ai);,63 二维数组,具有多个下标的数组称为多维数组,其中最常用的是二维数组,也就是具有两个下标的数组。二维数组可以看作具有行和列的平面排列形式,如矩阵。,6.3.1 二维数组的定义,定义二维数组的格式为:类型标识符 数组名整型常量表达式整形常量表达式;例如:int a23;表示定义了一个23(2行3列)的整型二维数组a,该数组有23=6个元素,分别是a00、a01、a02、a10、a11、a12。,在C语言中,可以把二维数组看成是一个特殊的一维数组,该一维数组中的每个元素又都是一个一维数组。例如,可把上面定义的数组a23看成是一维数组,它有两个元素a0、a1,但a0、a1本身并没有一个具体的值,它们又都是含有3个元素的一维数组,比如说:a0就是一个由a00、a01、a02这三个元素构成的一维数组。,6.3.2 二维数组元素的引用,二维数组元素的引用格式为:数组名行下标列下标,下标可以是整形常量、变量或表达式。例如,若有以下定义:int a23,i=1,j=2,k=0;则 a00,aik,aj-1i,a1j+k都是对a数组元素 的合法引用。注意:C语言中的数组下标是从0开始的,所以对于上面的数组a,其行下标范围为01,列下标范围为02,并不可能有a23这个元素。,6.3.3 二维数组的存储结构,二维数组在内存中占据的也是一段连续的存储空间。二维数组的存放方式有按行存放和按列存放两种。C语言中,二维数组是按行存放的,即先放第一行元素,再放第二行元素。,例如,若有定义:int a23;则数组a在内存中的存放形式如图6.2 所示。,图6.2 二维数组在内存中的按行存放结构,根据数组起始地址和数组元素的下标,可以计算出二维数组中任一个元素的存储地址。设有一个mn的二维数组a,其元素aij的地址计算公式如下:&aij=a+(i*n+j)*sizeof(数组类型),例如,假设上例中数组a的起始地址为2000,则a12的地址为:2000+(1*3+2)*2=2010。,6.3.4 二维数组的初始化,1、分行进行初始化。例如:int a23=1,2,3,4,5,6;注意:初始化的数据个数不能超过数组元素的个数,否则会出错。2、不分行的初始化。例如:int a23=1,2,3,4,5,6;3、只初始化部分数组元素。例如:static int a23=1,2,4;static int a23=1,2,4;4、初始化全部数组元素。此时可省略第一维的大小,但不能省略第二维的大小。例如:int a 3=1,2,3,4,5,6;,6.3.5 二维数组的典型实例,例6.7 二维数组的输入输出。由于二维数组有行、列两个下标,其输入输出就要用二重循环实现。在C语言中,二维数组是按行存放的,因此输入输出二维数组的二重循环,一般用行下标作为外循环变量,列下标则作为内循环变量。,以下是一个3*3数组的输入输出程序示例:,main()int a33,i,j;printf(“input array a:”);for(i=0;i3;i+)for(j=0;j3;j+)scanf(“%d”,输出完一行后换行,程序运行效果如下:input array a:1 2 3 4 5 6 7 8 9 output array a:1 2 3 4 5 6 7 8 9,例6.8 输入5个学生的学号和3门课的成绩,求每个学生的平均成绩。输出所有学生的学号、3门课的成绩和平均成绩。,分析:可以建立一个5行5列的单精度型2维数组,其中,第0列存放学号,1,2,3列分别存放3门课的成绩,4列存放平均成绩。首先,依次输入5个学生的学号和3门课的成绩,存放到数组的0,1,2,3列。然后计算3门课的平均成绩,并存放到4列。对每个学生重复以上操作。最后,依次输出所有学生的学号、3门课的成绩和平均成绩。,#include#define N 5main()int i,j;float aN5;printf(“input ID and grades:n”);for(i=0;iN;i+)for(j=0;j4;j+)scanf(%f,for(i=0;iN;i+)printf(%-5.0f,ai0);for(j=1;j5;j+)printf(%-7.1f,aij);printf(n);,printf(“ID grade1 grade2 grade3 averagen”);,程序运行效果如下:input ID and grades:201 67 78 51 202 69 98 74203 87 69 73204 39 68 76205 44 67 82ID grade1 grade2 grade3 average201 67.0 78.0 51.0 65.3202 69.0 98.0 74.0 80.3203 87.0 69.0 73.0 76.3204 39.0 68.0 76.0 61.0205 44.0 67.0 82.0 64.3,例6.9 产生一个44矩阵A,并将其行列互换后存放到矩阵B中。例如:,1 2 3 4 5 6 7 89 10 11 12 13 14 15 16 矩阵A,1 5 9 13 2 6 10 143 7 11 15 4 8 12 16 矩阵B,行列互换,对照A、B矩阵的元素,可以发现它们的对应关系如下:bij=aji 其中,i=0,1,2,3;j=0,1,2,3,for(i=0;iN;i+)for(j=0;jN;j+)bij=aji;printf(B array:n);for(i=0;iN;i+)for(j=0;jN;j+)printf(%-4d,bij);printf(n);,#include#define N 4main()int i,j,k,aNN;int bNN;printf(“input array A:n”);for(i=0;iN;i+)for(j=0;jN;j+)scanf(%d,6.4 字符数组与字符串,C语言中有字符常量、字符变量和字符串常量,但没有字符串变量。那如何存储字符串呢?在C语言中,可以用字符数组存放字符串。字符数组中的各个数组元素依次存放字符串的各个字符,字符数组的数组名代表该数组的首地址。这为处理字符串中个别字符和引用整个字符串提供了极大的方便。,6.4.1 字符数组的定义与初始化,字符数组就是其元素类型为字符类型的数组。字符数组的定义形式类似于数值数组的定义,只是数据类型改为char。例如:char str40;表示定义一个元素个数为40的字符数组,可以存放40个字符型数据。,与前面介绍的数值数组一样,字符数组也可以初始化。例如:char str7=s,t,u,d,e,n,t;,6.4.2 字符串,字符串是用双引号括起来的若干有效字符的序列。为了处理字符串方便,C语言规定以0 作为“字符串结束标志”,0占用一个字节,ASCII码值为0。对于字符串常量,C编译系统自动在其最后增加一个结束标志0。因此,像字符串“program”虽然只有7个字符,但实际要占用8个字节。C语言中没有字符串类型,对字符串的处理常常用字符数组实现,即将字符串存放在字符数组中。,例如,定义了一个字符数组:char s15;若要将字符串“How are you?”存放在数组s中,可以有以下方法:s0=H;s1=o;s2=w;s3=;.,其存储状态如图所示。s0 s1 s2 s3 s4.s14,但在实际应用中,常常需要将字符串作为一个整体来处理。为了便于处理字符串,C语言中允许使用字符串来初始化字符数组。例如:char s15=“How are you?”;可以省略花括号。例如上例可写成:char s15=“How are you?”;,其存储状态如图所示。s0 s1 s2 s3 s4 s14,自动在字符串未尾加上字符串结束标志0,6.4.3 字符串的输入和输出,1、用scanf()、printf()函数输入输出。,(1)用%c格式符逐个字符输入输出。例如:char a10;for(k=0;k10;k+)scanf(“%c”,*/,(1)用%c格式符逐个字符输入输出。例如:char a10;for(k=0;k10;k+)scanf(“%c”,*/,(2)用%s格式符整串输入输出。例如:char a10;scanf(“%s”,a);/*输入字符串存入*/printf(“%s”,a);/*输出a数组中的字符串*/,注意:,1.用格式符%s输入(出)字符串,其输入(出)项必须以字符串的地址形式出现。2.用格式符%s输出字符串时,从输出项提供的地址开始输出,直到遇字符串结束符0为止。3.格式符%s不能输入带空格的字符串。,2、用gets()和puts()函数输入输出字符串。要使用这两个函数,必须在程序开头加上命令行:#include“stdio.h”。(1)gets函数调用形式:gets(str);函数功能:从键盘输入一个字符串到str中,以回车换行作为输入结束符,并自动将换行符转换为0。,(2)puts函数调用形式:puts(str);函数功能:将str所代表的字符串输出,同时将0转换成换行符。,例6.12 字符串输入输出示例。#include main()char a15,b20=abcdn1234;gets(a);puts(a);puts(b);,程序运行时,输入:How are you,输出:How are youabcd 1234,6.4.4字符串处理函数,1.求字符串长度函数strlen()字符串长度是指字符串中的字符个数,但不包括字符串结束符。函数调用格式为:strlen(str)函数功能:求字符串str的长度,并将其作为函数值返回。,例如:int len;char s10=”student”;len=strlen(s);则len的值为7,而strlen(“hello world!”)的函数值为12。,2.字符串连接函数strcat()函数调用格式为:strcat(str1,str2)函数功能:将字符串str2连接到字符串str1的后面。,例如:char s112=English,s2=abc;strcat(s1,s2);连接后的串s1为Englishabc,串s2内容不变。,说明:str1一般为字符数组,要有足够的空间,以确保能放下连接后的字符串,,3.字符串复制函数strcpy()函数调用格式为:strcpy(str1,str2);函数功能:把源字符串str2复制到目标字符串str1中。,下列给字符数组赋值的方法是错误的:char s8;s=”program”;,但可以用以下方法:strcpy(s,”program”);,4.字符串比较函数strcmp()函数调用形式为:strcmp(str1,str2);函数功能:将字符串str1,str2进行比较,并将比较结果作为函数返回值:若str1=str2,函数的返回值为0;若str1str2,函数的返回值为正整数;,字符串之间的具体比较规则是:将两个字符串从左至右逐个字符比较,直到出现不同字符或遇到其中任何一个字符串的0为止,并将此时两个字符串中的对应位置的字符按ASCII码的大小进行比较,将将这两个字符的ASCII码的差值作为比较结果由函数值带回。,较两个字符串是否相同,一般使用下面的形式:if(strcmp(str1,str2)=0).;而不能直接判断:if(str1=str2).;,6.4.5 字符串数组的典型实例,例6.13 不使用strcpy函数,编程实现字符串复制功能。分析:先用两个字符数组分别存放源字符串和目标字符串。复制时,一边读源字符串的字符,一边把该字符存入目标字符串,这个过程可以在一个循环中实现,循环结束的条件是遇到源字符串末尾的字符串结束符。完整程序如下:,#includestdio.hmain()char s180,s280;int i;printf(input string s2:n);gets(s2);i=0;while(s2i!=0)/*逐个字符地复制*/s1i=s2i;i+;s1i=0;/*加上字符串结束符*/puts(s1);,例6.14 不使用strcmp()函数,编程实现两个字符串的比较。#include stdio.hmain()char s180,s280;int i=0,j;gets(s1);gets(s2);while(s1i,一、排序:1、冒泡排序2、选择排序,65 数组应用举例,1、选择排序 基本思想:从第1个数开始直到第n个数中找出最小的数,然后把它与第1个数交换位置。第一个数就确定了,以后的排序将不涉及该数,第2轮排序中,从第2个数开始到第n个数中找出最小的数,然后把它与第2个数交换位置,。第2个数也确定了,同样,以后的排序将不涉及该数,。第j轮排序中,从第j个数直到第n个数中找出最小的数,然后把它与第j个数交换位置。第j个数就确定了。第n-1轮排序中,第n-1个数与第n个数比较,找出最小值,然后把它与第n-1个数交换,完成排序操作。因此,n个数进行选择法排序,要经n-1轮排序处理。第j轮比较n-j次,至多交换一次(有时无需交换)。,#include#define N 5main()int i,j,t,p,aN;for(i=0;iN;i+)scanf(%d,二、查找算法1、顺序查找2、二分查找,二分查找:二分查找又称折半查找。它的基本思想是:设a是一个含有n个元素的升序数组,先取中间的元素an/2与x作比较,如果x=an/2,则查找成功,如果xan/2,则只需在后半部分搜索,每次查找范围大概可减少一半,是一种效率较高的查找算法,但是有一个前提:只能在有序的序列中进行查找。,#includestdio.h#define N 8 main()int left,right,mid,k,loca=-1;int aN=1,2,5,10,18,21,25,30;printf(“please input k:”);scanf(“%d”,三、输出杨辉三角前n行,n的值由键盘输入。11 11 2 11 3 3 11 4 6 4 15 10 10 5 1 杨辉三角是一个nn矩阵的下三角部分,第一列和对角线上的值均为1,即ai0=1,aii=1(0=in),其他部分的元素值则可由以下公式算出:aij=ai-1j-1+ai-1j(2=in,1=ji)。,#includestdio.h#define N 10 void main()int aNN,i,j,n;printf(“please input n:”);scanf(%d,