460 likes | 566 Views
第 7 章 数组. 主要内容 : 1 、一维数组 2 、二维数组 3 、字符数组和字符串 重点: 数组的定义和数组元素的引用. 数组概述. 数组:用于保存一批相同类型的数据 属构造类型 数组名:一批同类型数据共有的名字。 下标:数组中的各个数据用下标来区分。 例如,一组实验数据、一批学生成绩、表、矩阵等的处理,均用数组数据类型表示。. …. 7.1 一维数组. 例 7.1 输入 50 个整数,按逆序输出。 main()
E N D
主要内容: 1、一维数组 2、二维数组 3、字符数组和字符串 重点: 数组的定义和数组元素的引用
数组概述 • 数组:用于保存一批相同类型的数据 • 属构造类型 • 数组名:一批同类型数据共有的名字。 • 下标:数组中的各个数据用下标来区分。 • 例如,一组实验数据、一批学生成绩、表、矩阵等的处理,均用数组数据类型表示。
….. 7.1 一维数组 例7.1 输入50个整数,按逆序输出。 main() { int a[50], i; /* 定义数组a和循环变量i */ for(i=0; i<50; i++)/* 从键盘输入50个整数 */ scanf("%d", &a[i]); for (i=49; i>=0; i--)/* 逆序输出50个整数 */ printf("%4d", a[i]); } a[0] a[1] a[2] a[3] a[4]……a[49]
一维数组:每个数组元素只带有一个下标 • 一维数组的一般形式 : 类型说明符数组名[常量表达式] 如:float score[30]; float是类型说明符,定义了该数组中元素的类型。 score是数组名,是这一组数据共有的的名字; 30是常量表达式,表示数组的长度,即数组元素的个数。 也就是说:数组score 可以保存30个float型的数据。 注意:定义数组时长度必须是确定的值。 例如 : int aa[ ]; (error) int n; float xs[n]; (error)
数组元素的引用:数组名[下标] 如:a[i]是数组 a 中下标为 i 的数组元素; a[0]是下标为 0 的数组元素。 C语言的规定:数组的下标从0开始。 比如数组a[50]包含50个数组元素,分别是:a[0] a[1] a[2] …… a[49] 即下标的范围为0~49,不存在数组元素score[50]。 若使用a[50]是危险的,为什么? 一维数组的存储格式:每个数组占用一片连续的存储单元,按下标次序依次存放各元素。 如 int data[10]; 数组data占2*10=20个字节 data[0] data[1] data[2] …… data[9]
例7.3编程实现:输入某门课30人的成绩(0~100),要求将高于平均分的那些成绩打印出来。例7.3编程实现:输入某门课30人的成绩(0~100),要求将高于平均分的那些成绩打印出来。 分析数据结构:30人的成绩,类型相同,可用一维数组来保存。 分析算法: S1:输入30人的成绩并累加,用循环实现,循环体为“输入第 k 个人的成绩、并累加入部分和”; S2:求平均 S3 :求高于平均分的成绩。也用循环实现,循环体为“将第 k 个成绩平均分比较,若高,则输出”;循环条件为“k<30",k的初值为0,增值为“k++”.
#define N 3 main() { float score[N], sum, average; int k; printf("\n enter %d scores:", N); sum=0; for(k=0;k<N;k++) /*输入N个成绩,存入数组,并累加*/ { scanf("%f",&score[k]); sum=sum+score[i]; } average=sum/N; /*求平均*/ printf("\n average=%6.1f" , average); for(k=1;k<N;k++) if(score[k]> average) printf("\n %6.1f", score[k]); }
例7.4 用起泡法对N个整数由小到大排序。 • 起泡排序的基本思想是:相邻两个元素比较,按要求的顺序排放,比较一遍后,最大的元素被换到待排序数列的最后位置。对待排序序列依次重复上述过程,直至全部排完为止。 数据结构:N个整数用一维整型数组保存。 算法: (设N=5,待排序的数据为:3 2 10 8 1) 起泡排序过程为: 第一遍:2 3 8 1 10 第二遍:2 3 1 8 10 第三遍:2 1 3 8 10 第四遍:1 2 3 8 10 • 分析:当待排序数据个数n=5时,共排n-1=4遍,第i遍待排序序列是从第 1个元素到第n-i+1个元素。用双重循环实现。流程图:
例7.4 起泡排序。 #define N 5 main() { int a[ ]={0, 3, 2, 10, 8, 1}; int t,i,j; for(i=1;i<=N-1;i++) for(j=1;j<N-i+1;j++) if(a[j]>a[j+1]) /*若相邻两数逆序,则交换*/ {t=a[j];a[j]=a[j+1];a[j+1]=t;} printf("\n"); for(i=1;i<=N;i++) printf("%d ",a[i]); } • 数组a的长度为11,由初始值的个数决定。其中a[1]至a[10]为有效数据,a[0]未用,主要是为了符合我们的计数习惯。
例7.5 求某数列的前20项。该数列的特点: • f1=0 ( n=1) f2=1 (n=2) fn=fn-1+fn-2(n is odd)、fn=| fn-1 - fn-2 | (n is even) 分析: 数据结构: 该数列中的数据均为整型,可用整型的一维数组来保存。 算法: S1:从第三项开始,每一项都是前两项的和或差,可用循环实现:每循环一次计算出一项。求出前20项,分别存入数组中相应位置。 S2:输出数组中的每一个元素,用循环实现。
#include <math.h> main() { int f[21], k; f[1]=0; f[2]=1; for(k=3; k<=20; k+=2) { f[k]=f[k-1]+f[k-2]; f[k+1]=abs(f[k]-f[k-1]); } printf("\n number list:\n"); for(k=1; k<=20; k++) {printf("%10d", f[k]); if(k %6==0) printf("\n"); /*控制每行输出6个数*/ } }
一维数组的初始化 一维数组的初始化:定义数组时可以给部分或全部数组元素赋初值,即数组的初始化。 数组元素的初值依次放在一对花括号内。 (1)只给部分元素赋初值。 如: int x[20]={ 1,2}; 只给数组元素x[0],x[1]赋初值。 (2)对全部数组元素赋初值,数组长度可以省略,由初值的个数决定。如: int a[ ]={0, 3, 2, 10, 8, 1};
7.2 二维数组 • 用于描述类似矩阵、表之类的数据结构 • 特点:1)所有数据同类型,并且是由个数相等的几组数据组成。 2)每组数据表示一个完整的含义,可看作是一个整体。 • 比如,2*3的矩阵,共有6个数据,分成两组,每组3个数据,表示一行。可定义数组: int bb[2][3]; • 又如,某班30人,期末考4门课,他们的成绩都是float型,共有120个成绩,分成30组,每组4个,即表示某一个人的成绩;或分成4组,每组30个,即表示某一门课的成绩。 • 如定义数组: float score[30][4];可表示30人的成绩,每人4门课。 float sc[4][30];可表示4门课,每门可有30人的成绩。 比较:若用一维数组,能表示所有的数据,但不能表示出数据之间的分组关系。
例7.6输入某班(30人)期末考试4门课的成绩,计算出每人的平均成绩,按每行10个数据输出平均成绩。例7.6输入某班(30人)期末考试4门课的成绩,计算出每人的平均成绩,按每行10个数据输出平均成绩。 • 分析数据结构:30人4门课的成绩用二维数组表示,30人的平均成绩可用一维数组表示。 • 算法: S1:输入30人(每人4门课)的成绩。 S2:计算每人的平均成绩。 S3:输出每个人的平均成绩。 分别分析每一步如何实现?循环结构 • 程序:
main() /*例7.6*/ { float score[30][4], aver[30], sum; int i, j; for(i=0;i<30;i++) /*输入30人的成绩*/ {printf("\n enter 4 scores for no:%d\n",i+1); for(j=0;j<4;j++) /*输入一个人4门课的成绩*/ scanf("%f",&score[i][j]); } for(i=0;i<30;i++) { sum=0;/*循环体:计算每人的平均分*/ for(j=0;j<4;j++) sum+=score[i][j]; aver[i]=sum/4; } /*next page */
for(i=0;i<30;i++) /*输出每人的平均成绩*/ { if(i%10==0)printf(“\n”); /*控制每行10个数据*/ printf("%5.1f", aver[i]); } }
二维数组定义的一般格式: 类型名 数组名[常量表达式1][常量表达式2] 如 float score[30][4]; 其中的数组元素为: score[0][0], score[0][1], score[0][2],score[0][3] score[1][0], score[1][1], score[1][2],score[1][3] score[2][0], score[2][1], score[2][2],score[2][3] …… score[29][0], score[29][1], score[29][2],score[29][3] • 如表示2*3矩阵的数组bb的定义:int bb[2][3]; 其中的数组元素为:bb[0][0],bb[0][1],bb[0][2] bb[1][0],bb[1][1],bb[1][2] 说明:数组每一维的下标均从0开始。 ( 二维数组可看作特殊的一维数组,每个数组元素又是一维数组)
存储格式:二维数组同一维数组一样,也占用一片连续的存储单元,且按行存放。存储格式:二维数组同一维数组一样,也占用一片连续的存储单元,且按行存放。 • 数组名代表整个数组的起始地址,数组名与第一维下标代表某一行的起始地址。如score&score[0], score[i]&score[i][0] • 例7.7求已知矩阵a= 的转置矩阵 b= 分析两个矩阵元素的对应关系: a[i][j] 对应 b[j][i].
/*例7.7 */ main() {int a[3][3]={ {1,2,3}, {4,5,6}, {7,8,9} }; /*数组a初始化*/ int b[3][3], i, j; for(i=0;i<3;i++) /*求转置矩阵b*/ for(j=0;j<3;j++) b[j][i]=a[i][j]; printf(“\n matrix a:\n”); /*输出矩阵a(按行输出)*/ for(i=0;i<3;i++) { for(j=0;j<3;j++) printf("%5d",a[i][j]); printf(“\n”); /*输出一行后,换行*/ } /*接下页*/
/*上接页*/ printf(“matrix b:\n”); /*输出矩阵b*/ for(i=0;i<3;i++) {for(j=0;j<3;j++) printf("%5d",b[i][j]); printf("\n"); } }
二维数组的初始化: (1)给全部数组元素赋初值: int a[3][3]={{1,2,3},{4,5,6},{7,8,9}}; 等价于 int a[3][3]={1,2,3,4,5,6,7,8,9}; 或int a[ ][3]={1,2,3,4,5,6,7,8,9}; 其中第一维的长度可省略。(按行存储) (2)给部分数组元素赋初值:如 int b[2][4]={{1,2},{6,8}}; 结果只给4个元素赋值为: b[0][0]=1, b[0][1]=2, b[1][0]=6, b[1][1]=8
例7.8 求N*N矩阵的两条对角线元素之和。(以4*4矩阵为例) • 分析:对角线有两条,每条有两个对角线元素,第k行的两个对角线元素为a[k][k], a[k][n-k-1] • 设s1、s2分别表示两条对角线元素的部分和,则对于矩阵的每一行,都要将其对角线元素加入对应的部分和变量中。因此用循环结构。 • 循环体:s1+=a[k][k]; s2+=a[k][n-k-1] 条件:k<N k的初值:0 k的增值:k++
#define N 4 main() { int a[N][N], i, j, s1, s2; printf(“\n enter matrix :”); /*提示信息*/ for(i=0; i<N; i++) for(j=0; j<N; j++) /*输入一行*/ scanf("%d",&a[i][j]); s1=s2=0; for(i=0;i<N;i++) {s1+=a[i][i]; s2+=a[i][N-i-1]; } printf("\n sum=%d",s1+s2); }
7.3 字符数组和字符串 • 一维字符数组常用来表示字符串,每个数组元素保存一个字符。 • 例7.9下面程序将从字母‘b’开始的5个字符存入一个字符数组,并察看数组中的个元素。 #include "stdio.h" main() { char ss[5],ch='b'; int i; for(i=0;i<5;i++) { ss[i]=ch; ch++;} for(i=0;i<5;i++) /*输出每个字符数组元素*/ putchar(ss[i]); } 运行结果:bcdef
本例还可以用数组初始化的方式给数组元素赋值本例还可以用数组初始化的方式给数组元素赋值 #include "stdio.h" main() { char ss[5]={'b','c','d','e','f'}; int i; for(i=0;i<5;i++) putchar(ss[i]); } 结论:字符数组的定义、初始化及数组元素的引用方法与一般数组基本相同。 不同点:字符数组通常用于保存字符串,此时整个数组可以整体输入/输出。而整型、实型等数组只能引用其中的数组元素。 字符串存储时,系统自动在其末尾加上一个字符串结束标志:‘\0’,因此当用字符数组保存字符串时,字符数组的长度至少应定义为:字符串长度+1。
例7.11 输入一个字符串(设长度不超过80),统计其长度,并输出该字符串。 #include "stdio.h" main() { char str[80]; int i,n=0; printf("\n enter a line character:"); gets(str); /*字符串输入函数*/ i=0; while(str[i]!='\0') {n++;i++;} printf(“\n string:%s”,str); /*输出字符串str,用%s*/ printf("\n length:%d",n); } 运行时若输入:Turbo c 则输出: strng:Turbo c length:7
字符串输入/输出函数的使用: • (1)gets(str):从终端输入一个字符串,送入字符数组str。其中str代表数组str[80]的首地址。 用gets输入字符串时,以回车作为字符串的结束,并将回车符转换成字符串结束标志‘\0’。 • (2)scanf():输入不含空格的一个或多个字符串。 如例7.3.2中将gets(str)改为:scanf(“%s”,str); 若输入:Turbo c 则输出:string:Turbo length:5 Note:用scanf输入时,空格、回车均为数据的分隔符;当输入多个字符串时,每个字符串末尾自动 加上字符串结束标记‘\0’。
(3)printf()和 puts(str) : 例7.11输出两个字符串(分别用printf 和puts)。 #include "stdio.h" main() { char s1[30]="abcde", s2[20]="ghi"; printf("\n%s,%s",s1,s2); puts(s1); puts(s2); } 运行结果: abcde,ghiabcde ghi • Printf 可用控制符 %s 输出一个或多个字符串, • Printf 输出字符串时,字符串的结束标志‘\0’并不输出,也不会转换成‘\n’。 • puts(s1) ; 输出字符数组s1中的一个字符串,且将字符串结束标记‘\0’转换成‘\n’输出。
最常用的字符串操作函数 • Strcpy(s1,s2):拷贝字符串s2到s1。 • Strcat(s1,s2):把字符串s2接到s1的末尾。 • Strlen(s1):返回字符串s1的长度。 • Strcmp(s1,s2):比较字符串s1和s2的大小。若s1、s2相等,则返回0;若s1>s2则返回值大于0;若s1<s2则返回值小于0。 such as:"abcdef" less than "abkdeg" • Note: 以上参数均为待处理字符串的首地址。
例7.12 输入两个字符串,分别测试其长度,并比较两个字符串的大小。 • 分析: 1)输入两个字符串,分别存入字符数组str1、str2,用gets实现。 2)用函数strlen测试两个字符串的长度。 3)用函数strcmp比较两个字符串的大小。
#include “stdio.h“ /*字符串函数的综合使用*/ #define N 80 #include “string.h“ /*字符串处理函数的头文件*/ main() { char str1[N],str2[N]; int cmp; printf("\n input first string:"); gets(str1); printf(" input second string:"); gets(str2); printf("\n length1=%d, length2=%d", strlen(str1),strlen(str2)); cmp=strcmp(str1,str2); if(cmp==0) printf("\n equal."); else if(cmp>0) printf("\n str1 >str2."); else printf("\n str1<str2."); }
例7.14 输入一行字符,统计其中有多少个单词(单词之间用空格分隔)。 思路:输入一行字符串,存入一维字符数组;然后对每一个字符进行如下判断: 当前字符=空格? 若是,则未出现新单词,使word=0。 若不是,则根据前一字符是否为空格判断:若是(即 word=0),则新单词出现,num加1,word=1; 否则(即word=1),未出现新单词。 程序:x7_13.c
#include "stdio.h“ /*x7_13.c*/ main() { char str[81],c; int i,num=0,word=0; printf("\n enter a line character:"); gets(str); for(i=0;(c=str[i])!='\0';i++) if(c==' ') word=0; else if(word==0) { word=1; num++;} printf("\n there are %d words in the line.",num); }
特别提示 1)同一般数组一样,字符串也不能整体赋值,只能逐个引用数组元素。 如char s1[10],s2[40]; 则 s1=“ghhkji”; /*error*/ s2=s1; /*error*/ 可以用strcpy实现,或逐个数组元素赋值。 2)用字符数组存储字符串时,其元素的个数至少应比字符串的长度多1; 字符串处理时,以‘\0’为字符串结束,无需关心字符数组的实际长度。 例如: k=0; while(s[k]!='\0') k++;
上机调试 Errors: • 1)注意数组访问越界,或漏掉下标为0的元素。因为下标从0开始。 • 2)数组不能整体赋值。 • 3)定义数组时长度不确定。
练习 1. main() { int y[2][3]={2,34,6,8,10,12}; int i; for(i=0;i<2;i++) printf("%d,",y[1-i][i+1]); } 注意:二维数组的存储格式,下标从0开始。
2. #include "stdio.h" #define N 6 main() { char c[N]; int i; for(i=0;i<N;i++) c[i]=getchar(); for(i=0;i<N;i++) putchar(c[i]);printf("\n"); } /*注意:读取字符时回车符的处理。*/ 若运行时输入:ab c def 结果为:
3. main() { int n[3],i,j,k; for(i=0;i<3;i++) n[i]=0; k=2; for(i=0;i<k;i++) for(j=0;j<k;j++) n[j]=n[i]+1; printf("\n%d",n[1]); }/*注意下标的变化*/ 运行结果为:3
4. #include "stdio.h" main() {char ch[3][4]={"123","456","78"}; int i; for(i=0;i<3;i++) {printf("%s",ch[i]);puts(ch[i]);} } /*注意:字符串输出时‘\0’的处理。*/ 运行结果为: 123123 456456 7878
5.输入N位学生的成绩,统计各分数段的人数,统计结果存入数组,并输出。(统计原则:<60,60~69,70~79,80~89,90~99,100各为一段,N为符号常量)5.输入N位学生的成绩,统计各分数段的人数,统计结果存入数组,并输出。(统计原则:<60,60~69,70~79,80~89,90~99,100各为一段,N为符号常量) 分析:数据结构:N个学生的成绩、各分数段的人数分别用一维数组sc[N],count[11]存放。 算法:1)输入N个成绩 2)count 数组各元素清0 3)统计:对每一个成绩,判断其属于哪一分数段,然后将对应count数组元素加1。 4)输出count 中的各元素,即各分数段的人数。
6: 插入一个数到已排好序的数组中,使之仍然有序。 • 分析算法:设待插入的数为x,数组名为s,已按由小到大的顺序排列。 S1:找插入位置,方法是将x 与数组中的第一个元素比较,若s[0]>x,则x应插在s[0]处;否则,继续与下一个元素比较,直至找到第一个比x大的元素(x应插在该处)或数组中所有的元素都比较完(x应插在最后)。 S2:移动部分元素,为x腾出位置:若x的插入位置不在最后,则应将插入位置开始的数组元素后移一个位置。 S3:插入。流程图:
#define N 5 /* program l7_1_4.c */ main() /*设原数组中有5个元素。*/ { int s[N+1]={4,7,9,10,30}; int i,j,x; printf("\n enter a integer for insert:"); scanf("%d",&x); for(i=0;i<N;i++) /*找插入位置*/ if(x<s[i]) break; if(i<N) /*后移插入位置开始往后的元素*/ for(j=N-1;j>=i;j--) s[j+1]=s[j]; s[i]=x ; /*插入*/ for(i=0;i<N+1;i++) /*输出插入后的数组各元素*/ printf("%d ",s[i]); }
7. 输入10 个字符串(长度不超过30),按由小到大的顺序排序。 分析:10个字符串,用二维字符数组表示; 1)输入(用循环) 2)排序(同10个整数排序,用双重循环) 3)输出。 注意: 两个字符串交换时不能直接赋值,需用strcpy()函数。 程序:
综合练习 1、足球彩票系统:1)单注式:输入本期彩票的正确结果,输入若干单注式彩票,判断每一注是否中得一等或二等奖。(2)复式:输入本期正确结果,输入任意一张选票的选项,判断是否中得一等(十三场全猜对)、二等奖(猜错一场)。 2、自动阅卷系统 (1)单选题的批阅,每题2分。 (2)多选题的批阅: (A)错选一个即全错。(B)多选一个即全错,少选时选对一个的一分。每题 4分。
exercise • 1、上机调试题 • 2、习题 1 、 3、6、7