第 7章 数组主要内容,
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),
要求将高于平均分的那些成绩打印出来。
分析数据结构,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个数据输出平均成绩。
分析数据结构,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],
987
654
321
963
852
741
/*例 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为符号常量)
分析:数据结构,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