《[IT认证]厦门理工学院11级C语言 第4章数组.ppt》由会员分享,可在线阅读,更多相关《[IT认证]厦门理工学院11级C语言 第4章数组.ppt(53页珍藏版)》请在三一办公上搜索。
1、第四章 数 组,2,本章主要内容,4.1 一维数组的定义、初始化和引用,4.2 二维数组的定义、初始化和引用,4.3 字符数组与字符串,3,数组是C语言中提供的一种专门用来组织批量数据的数据类型,它可以将性质相同且需要共同参与某项操作的多个数据有效地组织起来,是一种应用十分频繁且非常重要的数据类型。对批量数据进行处理的情况在实际问题中经常会遇到,如对一组数据进行排序、求平均值;在一组数据中查找某一数值;矩阵运算;表格数据处理等等。假设要输入全年级100个学生的成绩,然后排出名次。显然定义100个变量来存放这100个学生的成绩是不现实也是不可取的,然而利用数组类型来解决这一问题却非常方便。所谓数
2、组就是一批同类型数据的有序集合,每个数组在内存中占一片连续的存储空间,用一个统一的数组名和下标来唯一确定数组中的元素,其中每一个元素通常称为下标变量。只有一个下标的数组称为一维数组,有二个下标的数组称为二维数组,依此类推。,4,4.1 一维数组的定义、初始化和引用,4.1.1 一维数组的定义方式 与前面章节介绍的基本数据类型变量的处理方式相同,数组作为带有下标的变量,也需要经历定义、初始化和引用3个阶段。一维数组定义的一般形式为:类型说明符 数组名常量表达式 其中:类型说明符用于说明数组的基类型,即数组中每一个元素的类型,可以是任一种基本数据类型或构造数据类型。数组名是用户定义的数组标识符,应
3、符合标识符的书写规定。在同一个函数中数组名不能与其它变量名相同。方括号中的常量表达式表示数组元素的个数,也称为数组的长度。例如:int a10;/*说明整型数组a,有10个元素*/float score20;/*说明实型数组score,有20个元素*/char letter26;/*说明字符数组letter,有26个元素*/,5,4.1 一维数组的定义、初始化和引用,对于数组类型说明应注意以下几点:1、允许在同一个类型说明中,说明多个数组和多个变量。如:int a,b,c,d,k110,k220;2、方括号中的常量表达式通常取整型常量或整型常量表达式(包括符号常量)。如:#define N 2
4、0/*用此命令行定义符号常量N,方便程序修改*/float scoreN;/*正确的定义方式,提倡使用*/C语言中不允许用变量下标形式对数组进行动态定义。如n为一个普通变量,定义:float scoren;/*不正确的定义方式*/是非法的。即使在数组定义语句前用变量初始化、用赋值语句或用输入函数scanf()对n进行了赋值,上面这条语句仍然是非法的。,6,4.1 一维数组的定义、初始化和引用,3、与其他高级语言(如PASCAL、BASIC)不同,C语言中规定数组元素的下标总是从0开始,例如int a10;说明整型数组a,有10个元素。这10个元素是:a0,a1,a2,a3,a4,a5,a6,a
5、7,a8,a9;注意最后一个元素是a9,而不是a10,该数组不存在数组元素a10。并且特别值得注意的是,C编译器对数组下标越界不作检查。如上述数组不存在数组元素a10,但是在程序中不小心引用了a10,程序编译时仍认为是合法的。但程序运行后可能会出现逻辑错误,因为a10 已不是数组中的元素,越界的操作可能破坏a9后面的数据或程序,要特别引起注意。,7,4.1 一维数组的定义、初始化和引用,4、在说明一个数组后,系统会在内存中分配一段连续的空间用于存放数组元素,并且数组名代表首地址。如说明int a10;则其在内存中的存放形式如图4-1所示:a0 a1 a2 a3 a4 a5 a6 a7 a8 a
6、9 图4-1 数组a在内存中的存放形式在内存中一维数组所占用的总字节数为:数组长度*sizeof(基类型),如在Turbo C环境中,整型数组a所占用的总字节数为:10*2=20。从上面可以发现每一个元素都相当于一个整型变量,其中可存放一个整型数值。,8,4.1 一维数组的定义、初始化和引用,4.1.2 一维数组的初始化 数组与简单变量一样,也可以对各元素初始化。数组初始化赋值是指在数组定义时给数组元素赋予初值。数组初始化是在编译阶段进行的,这样将减少运行时间,提高效率。初始化赋值的一般形式为:类型说明符 数组名常量表达式=初值表 其中初值表中的各数据值即为各元素的初值,各值之间用逗号分隔。例
7、如:int a10=0,1,2,3,4,5,6,7,8,9;相当于a0=0;a1=1;.a9=9;语言对数组的初始化赋值还有以下几点规定:1、可以只对部分元素赋初值。当 中值的个数少于元素个数时,只给前面部分元素赋值。例如:int a10=0,1,2,3,4;表示只给a0a45个元素赋值,而后5个元素自动赋0值。如果想使一个数组中全部元素值为0,可以写成:static int a10=0;/*第1个元素赋值0,后面9个元素自动为0*/,9,4.1 一维数组的定义、初始化和引用,2、只能给元素逐个赋值,不能给数组整体赋值。例如给十个元素全部赋1值,只能写为:int a10=1,1,1,1,1,1
8、,1,1,1,1;而不能写为:int a10=1;3、如给全部元素赋值,则在数组说明中,可不指定数组长度。例如:int a5=1,2,3,4,5;可写为:int a=1,2,3,4,5;4、当数组被说明为静态(static)或外部存储类型(即在所有函数外部定义)时,若不赋初值,则在程序编译阶段对数值型数组全部元素赋初值0,对字符型数组全部元素赋空字符(ASCII码为0的字符0)。,10,4.1 一维数组的定义、初始化和引用,4.1.3 一维数组元素的引用 数组是由若干个元素组成,数组名只能表示整个数组(首地址),而并不能说明其中某一个元素。如果希望指出具体的元素,需要按照下列格式书写:数组名下
9、标表达式 其中数组名是一个已经定义的数组,下标表达式的结果应该是一个介于数组下标取值范围内的整型数值,通常是一个整型表达式(其中可以包含整型常量或已赋值的整型变量)。数组元素也是一种变量,对数组元素的访问是通过下标来进行的,因此,可用循环语句操作数据,这在处理数据时带来很大方便。定义数组后,它所占有的存储单元的值是不确定的。在引用数组元素之前,必须保证数组元素已经被赋以确定的值。给数组元素赋值的方法有很多种,除了前面介绍过的数组初始化外,还可以象普通变量一样用赋值语句或用键盘输入的方式。,11,4.1 一维数组的定义、初始化和引用,例4-1 用数组来处理求Fibonacci数列问题(具体描述见
10、上一章的例3-20)。源程序如下:#include#define N 40main()int i;long int fN=1,1;/*数组初始化,f0及f1分别为1,其它为0*/for(i=2;iN;i+)fi=fi-2+fi-1;/*计算从第3个月起每月的总兔子对数*/for(i=0;iN;i+)if(i%5=0)printf(n);/*控制一行输出5个数*/printf(%12ld,fi);,说明:(1)在上一章的例3-20中主要是采用迭代递推的方法来求解,从而避免了使用大量变量所带来的不便。引入数组类型后,可以直接定义一维数组long int f40来存放所要求的40个数,并配合循环语句
11、通过一个控制下标变化的变量来访问一维数组的所有元素,显得直观、简单。由此可知,用一维数组来有序地存储和表示一组相同类型的数据是十分方便的。(2)本例中对数组元素的赋值主要通过初始化和赋值语句配合循环语句来进行。,12,4.1 一维数组的定义、初始化和引用,4.1.4 一维数组程序举例 例4-2 某电视台举办青年歌手大奖赛。假设有11个评委参与评分工作。计算每位歌手最终得分的方法是:首先去掉一个最高分和一个最低分,然后计算剩余9个分数的平均值,所得结果就是选手的最终得分。希望编写一个程序,帮助工作人员计算每个歌手的分数。分析:在进行程序设计之前需要考虑两个问题:(1)数据结构:程序所要处理的数据
12、如何存储和表示,本题中参加统计的数据是11位评委的评分,因此应该将这11个数据组织在一起形成批量数据。显然选用一维数组来存储比较合适。(2)算法:根据计算每位歌手最终得分的方法,关键是如何求出每位歌手所得的最高分和最低分?求最高分可以采用打擂台的方法:先假设第一个得分为最高分(即擂主),然后把其他的每个得分依次与最高分(即擂主)进行比较,若发现后面的得分高于前面的最高分,则将把最高分修改为后面的得分(产生新擂主)。当所有的得分都比较完毕,最高分也就求出来了(最终擂主产生)。求最低分的方法类似于求最高分,并且两者可以合并在同一个if语句中完成。,源程序如下:#include#define NUM
13、 11/*评委人数*/main()float scoreNUM,sum;/*一维数组score用于保存11位评委的评分*/int i,maxvalue,minvalue;printf(nEnter 11 score:);for(i=0;imaxvalue)maxvalue=scorei;else if(scoreiminvalue)minvalue=scorei;sum=sum+scorei;sum=(sum-maxvalue-minvalue)/(NUM-2);/*计算歌手的最终得分*/printf(nFinal score is%6.2f,sum);运行结果如下:Enter 11 scor
14、e:91 92 89 87 93 88 87 90 91 93 91Final score is 90.22,13,4.1 一维数组的定义、初始化和引用,例4-3 某电视台举办青年歌手大奖赛。假设有12位歌手参加决赛,经过评委评分工作,得到每位歌手最终得分。希望编写一个程序,帮助工作人员对12位歌手的最终得分按从大到小排列。分析:本例需要用到排序算法。排序和查找作为计算机应用中的两种最基本的操作,几乎在所有的数据库程序、编译程序、解释程序和操作系统中都可以见到它们的应用。所谓排序就是将一组无序的数列重新排列成升序或降序的过程。现有的排序算法有很多种类,如交换法、选择法、冒泡法、插入法等。本例采
15、用交换法来进行排序(从大到小),虽然此法执行效率较低,但其算法简单易懂,并且对理解选择法很有帮助。,14,4.1 一维数组的定义、初始化和引用,交换法排序借鉴了求最大值(降序时)、最小值(升序时)的思想。对n个数降序(或升序)排序可以分解为n-1趟(轮)比较来进行:第一趟:把第一个数依次和后面的数比较,如果后面的某数大于(小于)第一个数,则两个数交换,比较结束后,第一个数则是最大(最小)的数。第二趟:把第二个数依次和后面的数比较,如果后面的某数大于(小于)第二个数,则两个数交换,比较结束后,第二个数则是次大(次小)的数;依上类推 第n-1趟:从剩下的两个数中找出较大(小)的数,并将它交换到第n
16、-1个位置。至此,整个排序结束。为简单起见,假如现有6个整数:88,89,95,91,94,86,则利用交换法从大到小排序的过程如图4-2所示(阴影部门为有序部分,请读者自己认真理解):,15,4.1 一维数组的定义、初始化和引用,#include#define N 12main()float aN,temp;int i,j;printf(Please input numbers:);for(i=0;iN;i+)scanf(%f,运行结果如下:Please input numbers:90.5 90.22 91.4 90.1 89.5 88.7 93.2 94.2 92.4 90.2 93.2
17、 95.7 The sorted numbers:95.70 94.20 93.20 93.20 92.40 91.40 90.50 90.22 90.20 90.10 89.50 88.70 思考:排序中的外循环:for(i=0;iN-1;i+)中的i有什么作用?能否改为从1开始?说明:仔细研究上述算法可以发现,在每趟的比较中,若发现后面的数大就要交换位置,这样在整个算法中需要交换的次数太多,导致效率较低。其实完全可以在每趟的比较中先求出此趟中的最大值,再交换到相应位置即可,这样每趟至多做一次交换。虽然比较操作未能减少,但交换操作可以总体上减少,效率提高。这种改进的算法称为选择法排序。,16
18、,4.1 一维数组的定义、初始化和引用,例4-4 用选择排序法改写例4-3 分析:对n个数降序(或升序)排序可以分解为n-1趟(轮)比较来进行:第一趟:通过n-1次的比较,从n个数中找出最大(小)数的下标,通过下标找到相应的元素即最大(小)数,并将它交换到第1个位置。这样最大(小)数的数被安置在第1个位置上。第二趟:再通过n-2次的比较,从剩余的n-1个数中找出次大(小)数的下标,并将次大(小)数交换到第2个位置上。重复上述过程,共经过n-1趟排序后,排序结束。为简单起见,假如现有6个整数:88,89,95,91,94,86,则利用选择法从大到小排序的过程如图4-4所示(请读者自己认真理解):
19、,17,4.1 一维数组的定义、初始化和引用,#include#define N 12main()float aN,temp;int i,j,k;/*k用来记录得分最高元素的下标*/printf(Please input numbers:);for(i=0;iN;i+)scanf(%f,说明:在本程序每趟选择最高得分时,记录的是最高得分的下标,而不是最高得分本身,这样便于将最高得分交换到前面的位置。这是一个经常使用的编程技巧。从上面的几个例子可以看到,用数组来表示一组性质相同且需共同参与某项操作的批量数据是很方便的。通常,用下标表示批量数据中的不同个体,用数组元素记录批量数据中的每个数值。,1
20、8,本章主要内容,4.1 一维数组的定义、初始化和引用,4.2 二维数组的定义、初始化和引用,4.3 字符数组与字符串,19,4.2 二维数组的定义、初始化和引用,4.2.1 二维数组的定义假设一个学习小组有5个人,每个人有3门课的考试成绩。求全组各科的平均成绩和每人的总成绩,如图4-5所示。对于上述问题,若用一维数组来进行表示和处理,以课程(行)为单位至少要定义三个一维数组,以人(列)为单位至少要定义五个一维数组。并且在处理全组各科的平均成绩和每人的总成绩时,必须涉及到多个一维数组中的数据。显然这种表示方法不能很好地反映上述二维表中数据间的整体关系,处理起来也比较困难。这时引入两维数组可以方
21、便地表示和处理这个问题。,20,4.2 二维数组的定义、初始化和引用,二维数组定义的一般形式为:类型说明符 数组名常量表达式1常量表达式2其中:常量表达式1及常量表达式2分别表示数组的行数和列数,经常也称为行数常量表达式和列数常量表达式。例如:float s35;定义了一个3行5列实型数组s,有15个元素。逻辑上可以用一个矩阵(二维表格)来表示,如图4-6所示。把图4-5与图4-6比较,可以发现图4-6刚好可用来存储学习小组中有5个人,每个人有三门课的15个考试成绩。,说明:(1)与一维数组相同,二维数组的行标与列标也总是从0开始。(2)观察图4-6,可以发现同一行上的元素的行标是一样的,列标
22、从0开始增长。如第1行上的元素的行标都是0,若用b来代替s0,第1行上的元素可以改写为b0,b1,b2,b3,b4。显然这是一个一维数组,数组名为b,因此第1行可以看成一维数组,数组名为s0。所以二维数组s可以看成是由s0、s1、s2三个元素组成的一维数组,而每个元素s0、s1、s2本身又是一个包含5个元素的一维数组。因此我们可以把二维数组看作是一种特殊的一维数组,它的元素又是一个一维数组。,21,4.2 二维数组的定义、初始化和引用,必须强调的是:s0,s1,s2不能当作下标变量使用,它们是数组名,不是一个单纯的下标变量。图4-7数组a在内存中的存放形式(3)尽管二维数组在概念上是二维的,而
23、不象一维数组只是一个向量。但是,实际的硬件存储器却是连续编址的,也就是说存储器单元是按一维线性排列的。因此在语言中,二维数组与一维数组一样,在被说明为一个数组后,系统会在内存中分配一段连续的空间用于存放数组元素,并且数组名代表首地址。只是数组元素存放时是按行优先排列的,即先存放第一行,再存放第二行,依此类推。假设定义一个二行三列的整型数组int a23(int类型在TC软件中占二个字节的内存空间,所以每个元素均占有二个字节)。则其在内存中的存放形式如图4-7所示,它在内存中所占用的总字节数为:行数*列数*sizeof(基类型),即为:232=12(字节)。,22,4.2 二维数组的定义、初始化
24、和引用,4.2.2 二维数组的初始化 二维数组初始化也是在类型说明时给各下标变量赋以初值。二维数组可按行分段赋值,也可按行连续赋值。例如对数组float s35:(1)按行分段赋值可写为:float s35=80,75,92,61,65,71,59,63,70,85,87,90,76,77,85;(2)按行连续赋值可写为:float s35=80,75,92,61,65,71,59,63,70,85,87,90,76,77,85;这两种赋初值的结果是完全相同的。,23,4.2 二维数组的定义、初始化和引用,对于二维数组初始化赋值还可以用下列方式:(1)可以只对部分元素赋初值,未赋初值的元素自动
25、取0值。例如:int a33=1,2,3;是对每一行的第一列元素赋值,未赋值的元素取0值。赋值后各元素的值为:1 0 0 2 0 0 3 0 0 而对于int a33=0,1,0,0,2,3;赋值后的元素值为:0 1 0 0 0 2 3 0 0(2)如对全部元素赋初值,则第一维的长度可以不给出。例如:int a33=1,2,3,4,5,6,7,8,9;可以写为:int a3=1,2,3,4,5,6,7,8,9;注意:不能出现省略第二维长度的情况。,24,4.2 二维数组的定义、初始化和引用,4.2.3 二维数组元素的引用 二维数组的元素也称为双下标变量,其表示的形式为:数组名行下标列下标 其中
26、下标应为整型常量或整型表达式,其取值范围从0开始,分别到行数-1和列数-1。例如:a34 表示a数组中行标为3列标为4的元素。与一维数组相同,给二维数组数组元素赋值的方法有很多种,除了前面介绍过的数组初始化外,还可以象普通变量一样用赋值语句或用键盘输入的方式。如果要引用数组中的全部元素,只要利用循环语句即可实现,这是对数组中元素进行操作的基本算法。对于一维数组,已知道只需用单重循环就可完成;而对于二维数组则需要使用两重循环来完成,外循环控制行标变化,内循环控制列标变化。,下列程序段实现对数组float s35的动态赋值:for(i=0;i=2;i+)/*行标i从0开始变化到2*/for(j=0
27、;j=4;j+)/*列标j从0开始变化到4*/scanf(“%f”,&sij);/*二维数组元素如同普通实型变 量操作*/通过前面的学习,对数组也可以这样理解:单个变量描述了空间中“点”的概念,一维数组是对单个变量的扩展,它描述了空间中“线”的概念,而二维数组又对一维数组做了扩展,描述了空间中“平面”的概念。人们可以模仿二维数组的使用方法来实现多维数组等各种数据容器。例如,可用如下方法定义三维数组:float a234;多维数组元素在内存中的排列顺序为:第一维的下标变化最慢,最右边的下标变化最快。请读者自己画出上述三维数组元素在内存中的存放情况。,25,4.2 二维数组的定义、初始化和引用,4
28、.2.4 二维数组程序举例 例4-5 调用随机函数产生一个5行5列的矩阵a,要求每个元素值均为整数,并且10aij99,输出该矩阵。然后把矩阵a转置(行、列互换),再输出转置后的矩阵。分析:在C语言中,主要提供两个用于产生随机数的函数,它们的原型声明在stdlib.h头文件中:(1)函数原型:void randomize();功能是初始化随机数发生器。(2)函数原型:int random(int num);功能是产生一个介于0num的随机整数。5行5列的矩阵可以存放在一个二维数组中,数组元素值由上述的两个随机函数产生。矩阵转置(行、列互换)主要通过对数组元素的行标和列标的控制来实现。,26,4
29、.2 二维数组的定义、初始化和引用,#include#include#define N 5#define MAX 99#define MIN 10main()int i,j,aNN,temp;randomize();/*初始化随机数发生器*/clrscr();for(i=0;iN;i+)for(j=0;jN;j+)aij=MIN+random(MAX-MIN+1);/*随机产生10aij99*/printf(随机产生的a矩阵如下:n);for(i=0;iN;i+)for(j=0;jN;j+)printf(%5d,aij);printf(“n);for(i=0;iN;i+)for(j=i+1;j
30、N;j+)/*只扫描右上半部*/temp=aij;aij=aji;aji=temp;/*交换元素*/printf(a矩阵转置后如下:n);for(i=0;iN;i+)for(j=0;jN;j+)printf(%5d,aij);printf(n);,运行结果如下:随机产生的a矩阵如下:14 77 53 23 82 90 77 85 63 33 76 35 37 16 80 90 71 18 93 55 61 89 51 55 83a矩阵转置后如下:14 90 76 90 61 77 77 35 71 89 53 85 37 18 51 23 63 16 93 55 82 33 80 55 83思
31、考:为什么在矩阵转置(行、列互换)时内循环用语句for(j=i+1;jN;j+)来扫描其右上半部,是否可用语句for(j=0;jN;j+)替换?说明:在本例中,由于利用了随机数发生器,因此每次运行的结果也具随机性。其中必须利用randomize()来初始化随机数发生器,否则每次运行程序时,产生的随机数是同一个整数序列。在编写程序中,经常希望采用一些随机数运行程序,便于模拟实际问题中出现的随机状况。另外在程序调试中若需要较大数据量时,也可考虑使用随机数发生器来产生数据,以免去重复输入数据的麻烦。,27,4.2 二维数组的定义、初始化和引用,例4-6 一个学习小组有5个人,每个人有3门课的考试成绩
32、。求全组各科的平均成绩和每人的总成绩,如图所示。分析:要存放五个人三门课的成绩显然只要设一个二维数组s35(或s153)即可。接下来要计算全组各科的平均成绩和每人的总成绩,需要对上述的二维数组的每一行及每一列分别扫描计算,对所得结果的存放有两种方法:(1)再设二个一维数组ave3(用于存放各科平均成绩)及sum5(用于存放每人的总成绩)(2)直接对上述的二维表格进行扩充:增加一行用于存放每人的总成绩,再增加一列用于存放各科平均成绩,对应的二维数组改为s46(或s164)即可。比较上面两种方法,后者更为合适,也更利于对程序功能的扩展,如要在前面的基础上增加按五个人的总分进行排序等功能。,28,#
33、include#define M 4#define N 6main()int i,j;float sMN=80,75,92,61,65,71,59,63,70,85,87,90,76,77,85;/*以上用表中成绩对s数组部分初始化(前3行和前5列)*/*下面统计每门课程的平均成绩并填入二维数组中的最后一列*/clrscr();for(i=0;iM-1;i+)for(j=0;jN-1;j+)si5+=sij;/*对同一行元素扫描要保持行标不变,列标变化*/si5/=N-1;/*下面统计每人的总成绩并填入二维数组中的最后一行*/for(j=0;jN-1;j+)for(i=0;iM-1;i+)s3
34、j+=sij;/*对同一列元素扫描要保持列标不变,行标变化*/printf(n 张 王 李 赵 周 平均成绩);for(i=0;iM-1;i+)printf(n课程%d,i+1);for(j=0;jN;j+)printf(%-7.1f,sij);printf(n总成绩);for(j=0;jN-1;j+)printf(%-7.1f,s3j);,运行结果如下:张 王 李 赵 周 平均成绩课程1 80.0 75.0 92.0 61.0 65.0 74.6课程2 71.0 59.0 63.0 70.0 85.0 69.6课程3 87.0 90.0 76.0 77.0 85.0 83.0总成绩 238.
35、0 224.0 231.0 208.0 235.0说明:在程序的运行结果中,可以发现第一列只输出了很有规律的课程1、课程2、课程3,而要输出如表中的具体课程名,利用接下来将要介绍的字符数组与字符串就可以容易解决。,29,本章主要内容,4.1 一维数组的定义、初始化和引用,4.2 二维数组的定义、初始化和引用,4.3 字符数组与字符串,30,4.3 字符数组与字符串,在 C语言中,并没有设置专门的字符串数据类型,通常用一个一维字符数组来存放一个字符串,而二维字符数组则可以用来存放多个字符串,也称为字符串数组。4.3.1 字符数组与字符串的关系 字符串是由若干有效字符构成且以字符0作为结束标志的一
36、个字符序列,其中可以包括字母、数字、专用字符和转义字符等,并且用一对双引号括起来,如“China”。0作为字符串结束标志,在这里可不显式写出,C编译程序自动在其尾部添加字符0。该串的实际长度为5,但在内存中却占6个存储单元(此时包括0)。当一维字符数组中的最后一个元素存放空字符0时,该字符数组就是字符串,作为字符串的字符数组的初始化及引用等方法又可以不同于前面介绍的数值型数组,特别地此时的字符数组可以利用C编译系统中提供的字符串库函数进行操作,比较方便。,31,4.3 字符数组与字符串,4.3.2 字符数组的定义 形式与前面介绍的数值数组相同,只不过数组的基类型为字符类型。例如:char c1
37、0;定义c为一维字符数组,包含10个元素,可以存放10个字符型的数据,也可以存放实际长度最大为9个字符的字符串。因为串结束符0需占用一个位置,所以为了用一个字符数组来存储实际长度为N的字符串,要求字符数组的大小至少为N+1。由于字符型和整型通用,因此上面的定义也可改为:int cl0;但这时每个数组元素占2个字节的内存单元,浪费空间。类似地可以定义二维或多维字符数组。例如:char c510;定义了一个5行10列的二维字符数组,可以用来存放5个字符串,每个字符串实际长度最大为9个字符。,32,4.3 字符数组与字符串,4.3.3 字符数组的初始化 对字符数组初始化,通常可采用下面两种方式:1、
38、对字符数组的各个元素分别赋值。例如:char c12=H,e,l,l,o,W,o,r,l,d,!;把12个字符分别赋给c0到c11 12个元素。说明:(1)如果“”中提供的初值个数(即字符个数)大于数组长度,则作语法错误处理。(2)如果“”中提供的初值个数小于数组长度,则只将这些字符赋给数组中前面那些元素,其余的元素自动定为空字符(即0),这时字符数组就变成了字符串。如下面定义中c12的值为0:char c13=H,e,l,l,o,W,o,r,l,d,!;(3)如果“”中提供的初值个数与预定的数组长度相同,在定义时可以省略数组长度,系统会自动根据初值个数确定数组长度。如:char c=H,e,
39、l,l,o,W,o,r,l,d,!;数组c的长度自动定为12。用这种方式可以不必去数字符的个数,尤其在赋初值的字符个数较多时,比较方便。,33,4.3 字符数组与字符串,2、用字符串常量来给字符数组初始化。例如:char c=Hello World!;也可以省略花括弧,直接写成:char c=Hello World!;不是用单个字符作为初值,而用一个字符串(注意字符串的两端是用双引号而不是单引号括起来的)作为初值。上面的初始化与下面的初始化等价。char c13=H,e,l,l,o,W,o,r,l,d,!,0;而不与下面的等价:char c=H,e,l,l,o,W,o,r,l,d,!;前者的长
40、度为13,后者的长度为12。说明:(1)字符数组最后一个字符是否为0 完全根据需要决定。如:char c12=H,e,l,l,o,W,o,r,l,d,!;与 char a13=H,e,l,l,o,W,o,r,l,d,!,0;都是合法的,前者长度为12,后者长度为13;前者为普通字符数组,后者为字符串。但为了与字符串处理一致,通常在字符数组的后面加上一个0。(2)由于采用了0标志,所以在用字符串赋初值时一般无须指定数组的长度,而由系统自行处理。,34,4.3 字符数组与字符串,4.3.4 字符数组的引用 对是否含有结束标志符0的字符数组其引用方法是不同的。对不加结束标志符0的普通字符数组,在程序
41、中只可能逐个引用字符数组中的各个字符,而不能一次引用整个字符数组。而有加结束标志符0的字符数组是字符串,即可以逐个引用数组元素,也可以引用整个数组(此时使用数组名)。1、逐个引用字符数组中的各个字符。设已定义字符数组:char ch6=H,e,l,l,o,!;/*普通字符数组*/char str=“Hello!”;/*字符串*/现要把它们所包含的字符全部输出所对应的代码段分别如下:输出字符数组ch:for(i=0;i=5;i+)/*循环结束依赖于整个数组的长度*/printf(“%c”,chi);输出字符串str:for(i=0;stri!=0;i+)/*循环结束依赖于0的位置*/printf
42、(“%c”,stri);输出结果皆为:Hello!比较上面两种方法可以看出:字符串有了结束标志0后,在程序中往往依靠检测0的位置来判断字符串是否结束,就不必再用字符数组的长度来决定字符串的长度了。,35,4.3 字符数组与字符串,2、作为字符串的字符数组可以一次性整体引用 此时只需使用数组名即可,在遇到字符0时,表示整个字符数组(字符串)结束。如:char str=”Hello!”;printf(“%s”,str);输出结果与第1点中相同。说明:(1)0代表ASCII码为0的字符,因此要输出字符串str时用到的语句:for(i=0;stri!=0;i+)printf(“%c”,stri);中的
43、条件(stri!=0)也可以写成:(stri!=0)或(stri)。(2)从ASCII码表中可以查到,ASCII码为0的字符不是一个可以显示的字符,而是一个“空操作符”,即它什么也不干。用它来作字符串结束标志不会产生附加的操作或增加有效字符,只是一个供辨别的标志。,36,4.3 字符数组与字符串,4.3.5 字符数组的输入输出 类似与字符数组的引用,通常有以下两种方法:1、逐个输入输出字符数组中的各个字符,可以使用scanf()/printf()与getchar()/putchar()这两组库函数来完成。如:char ch6;(1)用格式输入输出函数scanf()及printf()配合格式符“
44、%c”scanf(“%c”,2、输入输出作为字符串的整个字符数组,可以使用scanf()/printf()与gets()/puts()这两组库函数来完成。,37,4.3 字符数组与字符串,如:char ch6;(1)用格式输入输出函数scanf()及printf()配合格式符“%s”scanf(“%s”,ch);printf(“%s”,ch);(2)用字符串输入输出函数gets()及puts(),使用时需在程序开头加上#include“stdio.h”输入函数gets()的一般形式:gets(字符数组)作用:从终端输入一个字符串到字符数组,并且得到一个函数值,该函数值是字符数组的起始地址。如:
45、gets(ch);输出函数puts()的一般形式:puts(字符数组)作用:将一个字符串(以0结束的字符序列)输出到终端。如:puts(ch);说明:(1)使用scanf和gets来输入字符串时,函数中的输入项是字符数组名,它本身就是地址,因此在引用ch时不能加地址符&。但用scanf函数来输入一个字符时,则必须加上&取其地址,如&ch1。,38,4.3 字符数组与字符串,(2)使用scanf和gets来输入字符串时,系统自动在后面加一个0字符,因此键盘输入的字符最多不超过“数组长度1”个字符。(3)注意:利用一个scanf函数可输入多个字符串,此时不同的字符串以空格来分隔。例如:char s
46、tr15,str25,str35;scanf(%s%s%s,str1,str2,str3);输入数据:How are you?输入后str1接收“How”,str2接收“are”,str3接收“you?”如果改为:char str13;scanf(%s,str);如果输入以下12个字符:How are you?实际上并不是把这12个字符加上0送到数组str中,而只将空格前的字符How送到str中,由于把“How”作为一个字符串处理,因此在其后加0。从上可知,无法利用scanf函数来接收带有空格的字符串。此时可利用gets函数来接收带有空格的字符串,因为gets函数所接收字符串回车为结束标志。如
47、:char str13;gets(str);输入:How are you!(回车)就可把此串赋值给str。但利用gets函数一次只能输入一个字符串,如不能写成:gets(strl,str2);,39,4.3 字符数组与字符串,(4)用printf()和puts()来输出字符串时,函数中的输出项是字符数组名,而不是数组元素名。写成下面这样是不对的:printf(%s,ch0);(5)printf()和puts()在输出字符串时将不显示结束标志0,编译系统自动将0转换成n,即输出完字符串后换行。(6)如果数组长度大于字符串实际长度,printf()和puts()在输出时也只输出到第一个0结束(即使
48、一个字符数组中包含一个以上0)。如:char c10=China;printf(%s,c);也只输出“China”五个字符,而不是输出10个字符。这就是用字符串结束标志的好处。(7)用printf()和puts()输出的字符串中可以包含转义字符。例如:char str=ChinanBeijing;puts(str);或 printf(“%s”,str);输出:China Beijing(8)用puts()一次只能输出一个字符串,不能写成:puts(str1,str2);而用printf()一次能输出多个字符串,如:printf(”%s%s%s”,str1,str2,str3);(9)由于C语言
49、用一维字符数组存放字符串,而且允许用数组名进行输入输出一个字符串,因此一维字符数组相当于“字符串变量”。,40,4.3 字符数组与字符串,4.3.6 字符串处理函数 1、字符串转换成数值类型 atof()、atoi()和atol()经常使用,它们的原型声明在stdlib.h中,调用格式分别为:atof(str);/*将字符串str转换成一个双精度的数值,返回类型是double*/atoi(str);/*将字符串str转换成普通整型,返回类型是int*/atol(str);/*将字符串str转换成长整型,返回类型是long*/比如一个学生具有如下信息:姓名(char name20)、学号(lon
50、g num)、年龄(int age)、总分(float score);若想对此学生信息进行赋值:char numstr20;gets(name);/*输入姓名*/gets(mumstr);/*以字符串形式输入学号*/num=atol(mumstr);/*将字符串形式的学号转化为长整型学号*/gets(mumstr);/*以字符串形式输入年龄*/age=atoi(mumstr);/*将字符串形式的年龄转化为整型年龄*/gets(mumstr);/*以字符串形式输入总分*/score=atof(mumstr);/*将字符串形式的总分转化为实型总分*/,可按下列形式输入数据:Li Ping(回车)2