《C语言程序设计07章数组.ppt》由会员分享,可在线阅读,更多相关《C语言程序设计07章数组.ppt(42页珍藏版)》请在三一办公上搜索。
1、第七章 数 组,数组的定义:是有序数据的集合。数组的特点:数组中的每个元素都属于同一数据类型。数组的访问:我们用一个统一的数组名和下标来唯一地确定数组中的元素。,基本类型:char int float double enum 构造类型:Array struct union 空类型void 指针类型:与以上各种类型相结合。,概 述,数据类型,7.1 一维数组的定义和引用,7.1.1 一维数组的定义定义方式:类型说明符 数组名 常量表达式例:int a10;它表示数组名为a,此数组有10个整型元素。如右图:,说明:1)数组名命名规则和用户标识符命名规则相同;2)数组名的常量表达式用方括弧括起来,而
2、不是用圆括弧。3)常量表达式表示元素的个数,即数组长度。例如:int a10;表示a数组共有10个元素,下标从0开始,分别为:a0,a1,a2,a3,a4,a5,a6,a7,a8,a9注意:没有a10。4)常量表达式中可以包括常量和符号常量,不能包含变量,即数组的大小应在编译阶段确定,不允许在程序运行过程中才确定。例如:int n;scanf(“%d”,是不合法的。,7.1.2 一维数组元素的引用,C语言规定,只能逐个引用数组元素而不能一次引用整个数组。一维数组元素的表示形式为:数组名下标 其中下标可以是整型(常量/变量)或整型表达式。例如:a0=a5+a7-a2*3;,例7.1 数组元素的引
3、用main()int i,a10;for(i=0;i=0;i-)printf(“%d”,ai);本例使a0到a9的值为09,然后按逆序输出这10个元素的值。,7.1.3 一维数组的初始化,(1)完全初始化:在定义一个数组时,对数组中所有元素都提供了初始化数据。例如:int a10=0,1,2,3,4,5,6,7,8,9;其中:a0=0,a1=1,a2=2,a3=3,a4=4,a5=5,a6=6,a7=7,a8=8,a9=9(2)部分初始化:提供了初始化数据,但数据不够,不够的部分取0。例如:int a10=0,1,2,3,4;其中:a0=0,a1=1,a2=2,a3=3,a4=4,a5=0,a
4、6=0,a7=0,a8=0,a9=0(3)没有初始化:只是定义了一个数组,但没有提供任何初始化数据,这时数组中每个元素的值是不确定的。例如:int a10;,(4)如果想把数组中所有元素初始化为0,可以写成:int a10=0,0,0,0,0,0,0,0,0,0;但不能写成:int a10=0*10;(5)若对数组中所有元素进行了初始化,可不指定数组长度。例如:int a5=0,1,2,3,4;可以写成:int a=0,1,2,3,4;对于后一种定义系统会默认a 数组的长度为5。但是如果被定义的数组长度与提供初值的个数不相同时,则数组长度不能省略。例如:int a10=0,1,2,3,4;只初
5、始化了前5个元素,后5个元素不确定。,例7.2 用数组来处理求Fibonacci数列问题。(P116-117和P124)main()int i;int f20=1,1;for(i=2;i20;i+)fi=fi-2+fi-1;/*一次循环只求一个数*/for(i=0;i20;i+)if(i%5=0)printf(“n”);printf(“%12d”,fi);,7.1.4 一维数组程序举例,这里只求了前20个数,与P117区别。,例7.3 用冒泡法对10个数排序(由小到大)。冒泡法:在要排序的数中,依次将相邻的两个数进行比较,若满足排序要求,保持两者位置不变,否则交换位置:,第一次比较,第二次比较
6、,第三次比较,第四次比较,第五次比较,第一次冒泡排序结果,第一次冒泡排序,从最前面两个元素开始,两两比较,直到最后一个元素比较完,最大的元素就到了最后的位置,这叫作“一趟”冒泡法排序。,从上述我们可以得出:如果有n个数参加冒泡排序,则要进行n-1趟比较。在第j 趟比较中有n-j+1个数参加排序,要进行n-j次两两比较。比如:本例中第2 趟有5个数参加排序,进行了4 次比较。,第一次比较,第二次比较,第三次比较,第四次比较,第二次冒泡排序结果,第二次冒泡排序,main()int a11;int i,j,t;printf(“input 10 numbers:n”);for(i=1;iai+1)t=
7、ai;ai=ai+1;ai+1=t;printf(“the sorted numbers:n”);for(i=1;i11;i+)printf(“%d”,ai);,for循环的嵌套,7.2 二维数组的定义和引用,7.2.1 二维数组的定义一般形式:类型说明符 数组名 常量表达式 常量表达式 例:float a23,b510;此句定义了一个名为a的2行3列的实数数组和一个名为b的5行10列的实数数组。注意:不能写成:float a2,3,b5,10;说明:(1)在C语言中,可把二维数组看成一个特殊的一维数组:它的一个元素又是一个一维数组,即可看成是一维数组的嵌套。比如:我们可以把二维数组a23看成
8、下图所示的嵌套关系。,相应的内存空间,如右图:二维数组是多维数组的基础。其它多维数组的定义和元素存放形式与二维数组类似。,(2)二维数组的存放顺序:C语言中,二维数组的元素在内存中是按行依次连续存放的,即先顺序存放第一行的元素,再存放第二行的元素,以此类推。例如:a23的存放形式为,a0 a00,a01,a02 a1 a10,a11,a12,a,7.2.2 二维数组的引用,二维数组元素的表示形式为:数组名 下标 下标 例如:a23表示的是a二维数组的第3行第4列的元素。注意:数组超界的问题。假如有程序段:int a34;a34=13;引用a34便超出了所定义的边界,此时系统不会报错,同样把13
9、赋值到指定单元,但此单元却可能是我们的其它有用数据。这样,便很容易造成意想不到的错误,甚至死机。同学们一定要小心,这是大家经常感到莫名其妙的地方。,7.2.3 二维数组的初始化,(1)按行对二维数组进行初始化。例如:int a34=1,2,3,4,5,6,7,8,9,10,11,12;(2)也可将所有数据写在一个花扩弧内,系统会以按行存放的顺序依次对各数组元素进行初始化。例如:int a34=1,2,3,4,5,6,7,8,9,10,11,12;(3)和一维数组一样,也可只对各行中前面部分元素初始化。例如:int a34=1,5,9;它的作用是只对每行第一列元素进行初始化,其它元素自动为0,结
10、果为:,也可以只对前面各行中的元素初始化,其它元素自动为0:int a34=1,2,3;结果为:,(4)若要对全部元素都初始化,则定义数组时对第一维的长度可以不指定,但第二维的长度不能省略。例如:int a34=1,2,3,4,5,6,7,8,9,10,11,12;可以写成:int a 4=1,2,3,4,5,6,7,8,9,10,11,12;,注意:初始化只能从“前面”开始,即必须先初始化前面各行再初始化后面各行,也必须先初始化每行的前面部分再初始化后面部分。不能跳过前面部分去初始化后面部分。(P128有错)如:int a5=1,3,4,5;和int b34=1,2,3;是错的。,例7.4
11、将二维数组 行和列元素互换,存到另一个二 维数组中。,7.2.4 二维数组程序举例,main()int a23=1,2,3,4,5,6;int b32,i,j;printf(“array a:n”);for(i=0;i=1;i+)/*控制行*/for(j=0;j=2;j+)/*控制列*/printf(“%5d”,aij);bji=aij;printf(“n”);,printf(“array b:n”);for(i=0;i=2;i+)for(j=0;j=1;j+)printf(“%5d”,bij);printf(“n”);,例7.5 有一个3X4的矩阵,要求编程求出其中最大的那个元素的值,以及其
12、所在的行号和列号;main()int i,j,row=0,colum=0,max;/*定义标记变量*/int a34=1,2,3,4,9,8,7,6,-10,10,-5,2;max=a00;for(i=0;imax)max=aij;row=i;colum=j;printf(“max=%d,row=%d,colum=%dn”,max,row,colum);,7.3 字符数组,7.3.1 字符数组的定义字符数组的定义与前面所述数组的定义类似。例如:char c10;c0=S;c1=I;c2=L;c3=I;c4=C;c5=T;c6=_;c7=L;c8=A;c9=N;上句的作用为:定义C为字符数组,包
13、含10个元素。赋值后的状态为:,在C语言中,字符型和整型是互相通用的,因此上述定义语句可以写为:int c10;但不推荐这样定义。原因是不便于作为字符串进行控制。,7.3.3 字符数组的初始化(一),对于字符数组的初始化,最容易理解的方式就是像我们前面所讲的数值型数组的初始化方式一样,将字符逐个赋给数组中的各元素。例如:Char c10=M,y,C,H,I,N,A;,说明:1)在初始化时,若花括弧中提供的初值个数大于数组的长度,则按语法错误处理。2)若小于数组长度,则将用户提供的初值先依次赋给前面的元素,对余下的元素系统自动赋 0(对应空字符)。3)若等于欲定义的数组长度时,可省略数组长度。,
14、这里存放的是空格的ASCII码,40是八进制040 的缩写,对应十进制数32。,4)也可以定义和初始化一个二维字符数组,方法与前面二维数值型数组类似。如:存放一个由“*”组成的菱形。char diamond55=,*,*,*,*,*,*,*,*;,7.3.3 字符数组的引用 可以引用字符数组中的一个元素,得到一个字符。例7.6:输出一个字符串。main()char c10I,a,m,a,b,o,y;int i;for(i=0;i10;i+)printf(“%c”,ci);printf(“n”);,例7.7:输出一个由“*”组成的菱形。main()char diamond55=,*,*,*,*,
15、*,*,*,*;int i,j;for(i=0;i5;i+)for(j=0;j5;j+)printf(“%c”,diamondij);printf(“n”);运行结果为:*,7.3.4 字符串和字符结束标志 在C语言中,将字符串作为字符数组来处理。通常,我们关心的是有效字符的长度而不是字符数组的长度。例如,定义一个字符数组长度为100,而实际有效字符可能只有40个。为了测定有效字符的实际长度和便于对一个连续的字符序列进行处理,C语言规定:以空字符0作为一个连续字符序列的“结束标志”,称该字符序列为字符串。也就是说,当处理一个字符串时,若遇到字符0,则表示该字符串结束。在处理字符串常量时,系统也
16、自动加一个0作为结束符。例如“C Program”共有9个字符,但在内存中占10个字节,最后一个字节0是由系统自动加上的。,字符数组初始化(二):用字符串常量来初始化字符数组。例如:char c=“I am happy”;也可以省略花括弧,直接写成 char c=“I am happy”;不是用单个字符作为初值,而是用一个字符串(注意字符串的两端是用双引号而不是单引号括起来的)作为初值。显然,这种方法直观、方便、符合人们的习惯。注意:上述字符串的长度是11而不是10。因为字符串常量的最后由系统加上了一个0。上述的初始化与下面的初始化等价。char c=I,a,m,h,a,p,p,y,0;而与下
17、面的初始化不等价:char c=I,a,m,h,a,p,p,y;前者的长度为11,后者的长度为10。,需要说明的是:字符数组并不要求它的最后一个字符必须是0,用户可以自行另外指定结束标志,甚至可以不包含结束标志。像以下面这样定义完全是合法的。char c5=C,h,i,n,a;但是,由于系统对字符串的处理默认是以0作为结束标志,因此,为了使处理方法统一,不致引起误会,我们在对字符数组赋值时,通常都会人为地加上结束标志0。如:char c6=C,h,i,n,a,0;,7.3.5 字符数组的输入输出,字符数组的输入输出可以有两种方法:(1)逐个字符输入输出。用格式符“%c”输入或输出一个字符。如:
18、main()char c10I,a,m,a,b,o,y;int i;for(i=0;i10;i+)printf(“%c”,ci);printf(“n”);(2)将整个字符串一次输入或输出。用“%s”格式符。如:char c=“china”;printf(“%s”,c);,关于用%S输出字符串的说明:1、printf(“%s”,c);中的c 是一个已定义的字符数组名,它代表该数组的起始地址。注意不能写成:printf(“%s”,c0);,2、此语句输出一字符串的执行过程是:先按字符数组名c找到该字符数组的起始地址,然后逐个输出其中的字符(系统在输出n时,会自动再加上一个r),一旦遇上0便结束输出
19、(但输出的字符中不包括结束符0)。,那请问1:如果字符数组长度大于字符串实际长度,此时是否全部输出字符数组中的各元素?又请问2:如果一个字符数组中含有多个0,则输出到哪里结束?,关于用%S输入字符串的说明:1、可以用scanf(“%s”,c)函数来输入一个字符串,其中c是已经定义的一个字符数组名。,2、此语句的执行过程是:先等待用户从键盘输入字符串,用户从键盘输入的内容要先送至输入缓冲区,当用户按下回车时,系统停止接收该次输入的数据(从键盘按下一次Enter键,它代表n和r两个字符,但系统只保存一个n字符),然后从缓冲区中第一个不是分隔符(空格、Tab或回车)的字符开始读取数据(字符串),再从
20、c的起始地址开始依次往后赋值,直到遇上分隔符为止,并在字符数组中自动添加一个0,但缓冲区中的该分隔符依然存在。注意:用scanf 函数输入字符串与数值型数据时,分隔符相同。,因此,从键盘输入的字符串在赋给字符数组c时,系统不会识别c 的定义长度。在调试程序时应注意所输入的字符长度不能超过所定义的字符数组长度,否则会数组超界,出现意想不到的错误,甚至死机。,7.3.6 字符串处理函数,在C的函数库中提供了一些用来处理字符串的函数,使用方便。几乎所有版本的C都提供这些函数。下面是几种常用的函数。1.puts(字符数组名)作用:将一个字符串(以0结束的字符序列)输出到终端。由于可以用printf函数
21、输出字符串,因此puts函数用得不多。用puts输出的字符串中也可以像printf一样包含转义字符。例如:char str1=“Chinan”,str2=“Beijing”;puts(str1);puts(str2);输出:China(注意:这里空了一行!)Beijing,区别:用puts输出一个字符串时,它会将结束标志0转换成n来输出,而printf不会对0进行处理。,2.gets(字符数组名),作用:从终端输入一个字符串到字符数组,并且得到一个函数值。该函数值是字符数组的起始地址。如执行下面的函数:gets(str)从键盘输入:Come on!将输入的字符串”Come on!”送给字符数组
22、str(请注意送给数组的共有9个字符,而不是8个字符),函数值为字符数组str的起始地址。一般利用gets函数的目的是向字符数组输入一个字符串,而不大关心其函数值。,区别:gets是从缓冲区的第一个字符开始读起,直到n为止,并把n读走(缓冲区中不再存在)转换成0,再赋给字符数组作为结束标志;而scanf函数是从缓冲区中第一个不是空格、Tab和n的字符开始,直到空格、Tab或n为止,且不读走该空格、Tab或n(缓冲区中的该空格、Tab或n依然存在)。,注意:用puts和gets函数只能输入或输出一个字符串。,3.strcat(字符数组名1,字符数组名2)全称string catenate作用是:
23、把字符串2追加到字符串1后面,结果放在字符数组1中,字符数组2不变,函数调用后得到一个函数值(字符数组1的地址)。例如:char str130=“Peoples Repubnic of”;char str2=“China”;printf(%s,strcat(str1,str2);输出:Peoples Republic of China 连接前后的状况见下图所示。,说明:(1)字符数组1必须足够大,以便能容纳追加后的新字符串。上例中定义str1的长度为30,是足够大的。(2)追加前两个字符串的后面都有一个0,追加时将字符串1后面的0覆盖掉,只在新串最后保留一个0。,4.strcpy(字符数组名1
24、,字符串或字符数组名2)作用:将字符串2复制到字符数组1中,变相实现“字符串的赋值”。例如:char str110,str2=“China”);strcpy(strl,str2);执行后,str1的状态右图所示。说明:(1)字符数组1的长度不应小于字符串2的长度,以便容纳被复制的字符串。(2)strcpy的第一个参数必须写成数组名形式(如str1),第二个参数可以是字符数组名,也可以是一个字符串常量。如:strcpy(str1,“China”);(3)复制时连同字符串2后面的0一起复制到字符数组1中。(4)不能用赋值语句将一个字符串常量或字符数组直接赋值给另一个字符数组。下面两行都是不合法的:
25、str1=“China”;str1=str2;而只能用strcpy函数处理。赋值语句只能用来将一个字符赋给一个字符变量或字符数组的一个元素。注意:strncpy(str1,str2,2);只将str2的前面2个字符拷过去,不拷贝空字符0。,5.strcmp(字符串或字符数组名1,字符串或字符数组名2)作用:比较两个字符串的大小。例如:#include main()char str1=computer;char str2=compare;printf(%d n,strcmp(str1,str2);printf(%d n,strcmp(command,str1);结果:20-3比较的结果由函数值带
26、回。(1)如果字符串1字符串2,函数值为0.(2)如果字符串1字符串2,函数值为一正整数。(3)如果字符串1字符串2,函数值为一负整数。注意:对两个字符串比较,不能用:if(str1=str2)printf(“yes”);而只能用:if(strcmp(str1,str2)=0)printf(“yes”);,6.strlen(字符数组)作用:测试字符串长度的函数,函数的值为字符串中的实际字符长度,不包括0在内。如:char str10=“china”;printf(“%d”,strlen(str);输出结果不是10也不是6,而是5。也可以直接测字符串常量的长度。如:strlen(“china”)
27、;结果也是5。7.strlwr(字符串)作用:是将字符串中的大写字母转换成小写字母。8.strupr(字符串)作用:是将字符串中的小写字母转换成大写字母。,lower case:小写字母,upper case:大写字母,强调:库函数并非C语言本身的组成部分,而是人们为了使用方便,将一些常见问题编写成的公共函数,提供给大家使用。每个系统提供的函数数量、函数名和函数功能都不尽相同,使用时要小心,心要时查一下库函数手册。这里,我们均以Turbo C 2.0为准。当然,有一些基本的函数,不同的系统所提供的是相同的(包括函数名和函数功能),这就为程序的通用性提供了基础。,解题的思路:1、只有在由空格切换
28、到非空格时,才能认为是新单词的开始。比如:,7.3.7 字符数组应用举例,例7.8 输入一行字符,统计其中有多少个单词,单词之间用空格分隔开。,HiGoodLuck,2、前一个字符是否为空格可以用一个标记变量word来标记:若word=0,则表示前一个字符是空格;若word=1,则表示前一个字符为非空格。,新单词开始,单词的继续,变量定义:i 为循环变量,num统计单词个数,word标记前一字符是否为非空格,c为当前字符,算法见下图:,#include main()char string81;int i,num=0,word=0;char c;gets(string);for(i=0;(c=s
29、tringi)!=0;i+)if(c=)word=0;else if(word=0)word=1;num+;printf(“There are%d words in the line.n”,num);,源程序如下:,请问:可用scanf(“%s”,string);替换吗?,例7.9 有3个字符串,要求找出其中最大者。今设一个二维字符数组str,大小为3X20,即有3行20列,每一行可以容纳20个字符。这样,我们可以把str0,str1,str2看作是3个一维数组的数组名(称为“行地址”),也同样可以用gets函数分别给它们录入字符串。main()char string20;char str320;int i;for(i=0;i0)strcpy(string,str0);else strcpy(string,str1);if(strcmp(str2,string)0)strcpy(string,str2);printf(“n the largest string is:n%sn”,string);,str,第七章 数组 小结,7.1 一维数组的定义和引用7.2 二维数组的定义和引用 7.3 字符数组,