第 7 章 数 组一、总结:
前面的程序设计,一般是根据需求,先作几个变量的定义,然后对这些变量作相应的运算即可得结果 。
二、问题:
从键盘输入 100个数,原样输出;
输入 100个数,相反的次序输出 。
引言数组,
具有相同数据类型的一组变量的有序集合,这些变量在内存中占有连续的存储单元。
数组中的每一个数据?称有一个相对位置序号,
即数组元素的下标。
数组元素数组分量下标变量
7.1 一 维 数 组一维数组的 定义:
只有一个下标的数组。
定义格式,
类型标识符 数组名 [元素个数 ];
在 C 语言中,数组必须显示地说明,以便编译程序为它们分配内存空间。
例 1,int a[5];
定义一个 整型 数组(数组的元素为整型);
数组名称为 a;代表数组的首地址(常量);元素个数为 5;
分别为 a[0],a[1],a[2],a[3],a[4];
每个元素都可作为一个 整型变量 来使用;
如,a[0]=5; a[1]=4; a[2]=3; a[3]=2; a[4]=1;
注意:
1、类型标识符:数组 元素 的类型
2、数组名的命名规则与 标识符 的命名规则要同。
3、数组“元素个数”即数组长度,只能是一个 整型常量表达式 或 整型符号常量表达式 。
4、下标是数组元素在数组中的顺序号,从 0开始。
5、下标只能是 有序类型 的数据(常量、变量和表达式)。
没有 a[5]
例 2:试判断下列数组定义是否合法,
– int student[35];
– char name[20];
– float score[35];
– #define student 35
float no_student[student];
int score_student[student*3];
– int person(10);
– int n=10,a[n];
3,数组元素的引用定义了数组以后,就可使用它了 。
规定,只能引用数组元素,不能一次引用一个数组 。
引用方法:数组名 [下标 ]
如,student[1],name[15],name[i]等 。
例 3、从键盘输 15个整数,然后反序输出 。
分析:不用数组行否?行,但非常繁杂。下面用数组编程:
#include <stdio.h>
main()
{ int i,a[15];
for(i=0;i<15;i++) scanf(“%d”,&a[i]);
printf(“\n”);
for(i=14;i>=0;i--) printf(“%4d”,a[i]);
}
输入,1 2 3 4 5 6 7 8 9 10 11 12 13 14 15?
输出,15 14 13 12 11 10 9 8 7 6 5 4 3 2 1
注意,1、循环控制变量的初值、终值及控制条件。
2、不能整体输出数组例,printf(“%d”,a);是错误的 。
不能以“,”分开二、一维数组的存储结构与初始化
1、一维数组的存储结构数组变量 在内存中分配一片连续的存储单元,数组元素按数组下标从小到大连续存放 。
例 int a[5];
a[0]
a[1]
a[2]
a[3]
a[4]
a[0]
a[1]
a[2]
a[3]
a[4]
0
1
2
3
4
初始化内存
2、一维数组的初始化含义,在定义数组的同时,对数组各元素指定初值。
初始化是编译阶段完成的,不占用运行时间。
注意,用赋值语句或输入语句也可给数组元素指定初值,
但赋值是在运行时完成的,占用运行时间。
对数组初始化的几种方法:
①在定义数组时,对 全部 数组元素赋予初值,此时可省数组长度,系统自定。
例,int a[ ]={0,1,2,3,4}; 等价于 int a[5]={0,1,2,3,4};
② 在定义数组时,对 部分 数组元素赋予初值。
例,int a[5]={1,2},b[5]; static int c[5];
a[0]=1,a[1]=2;其它为 0; c[0]~c[4]为 0。
b[0]~b[4]不确定。
例,#include <stdio.h>
main()
{ int i,a[5]={3,4,5},b[5];
printf(“\narray a is:,)
for(i=0;i<5;i++) printf(“%6d”,a[i]);
printf(“\narray b is:,)
for(i=0;i<5;i++) printf(“%d,”,b[i]);
}
运行结果:
array a is,3 4 5 0 0
array b is,-32,1398,40,1170,454,
例:从键盘上输入 10个整数,用选择法将其按由小到大的顺序排列并输出。
基本思想:
( 1)从第0个位置到第9个位置中选择出最小的一个与第
0个位置的数交换。
( 2)从第1个位置到第9个位置中选择出最小的一个与第
1个位置的数交换。

(9)从第8个位置到第9个位置中选择出最小的一个与第8
个位置的数交换。
例,5 13 3 9 32 22 8 1 23
21 排序过程如下:
5 13 3 9 32 22 8 1 23 21
① 1 13 3 9 32 22 8 5 23 21
② 1 3 13 9 32 22 8 5 23 21
③ 1 3 5 9 32 22 8 13 23 21
④ 1 3 5 8 32 22 9 13 23 21
⑤ 1 3 5 8 9 22 32 13 23 21
⑥ 1 3 5 8 9 13 32 22 23 21
⑦ 1 3 5 8 9 13 21 22 23 32
⑧ 1 3 5 8 9 13 21 22 23 32
⑨ 1 3 5 8 9 13 21 22 23 32
程序如下,
#include <stdio.h>
main()
{int i,j,t,a[11];
for(i=1;i<11;i++)scanf(“%d”,&a[i]);
for(i=1;i<=9;i++)
for(j=i+1;j<=10;j++)
if(a[i]>a[j])
{t=a[i];a[i]=a[j];a[j]=t;
}
for(i=1;i<11;i++)printf(“%6d”,a[i]);
}
内循环:在
( i,10)内选择最小数外循环:
控制选择的次数分析:
从程序可知:
1、程序使用两重循环来实现排序。
2、其中,外循环控制排序趟数。若数组有 N个元素,则共进行 N-1趟排序。第一趟,I=0;第二趟,I=1,···
3、其中,内循环完成在 [I,9 ]的区间内选择最小数。比较次数随趟数增大而减少。
4、在每一趟选择中,当后面元素较小时,马上进行交换。
而这种交换是不必要的。事实上,只要记住较小元素的位置,即下标,在内循结束后做一次交换即可,这样可大大节省程序运行时间。
改进程序如下:
#include <stdio.h>
main()
{int i,j,k,a[10];
for(i=0;i<10;i++)scanf(“%d”,&a[i]);
for(i=0;i<9;i++)
{k=i;
for(j=i+1;j<10;j++)
if(a[k]>a[j])k=j;
if(k!=i){t=a[i];a[i]=a[k];a[k]=t;}
}
for(i=0;i<10;i++)printf(“%6d”,a[i]);
}
内循环 外循环
7.2 二 维 数 组
1、二维数组的定义和引用定义,
类型标识符 数组名 [行数 ][列数 ];
二维数组可看作一个二维表格,由行、列组成。
例 1,int a[2][3];
定义了一个 2?3的数组 a,即数组为 2行 3列,可存放 6个整型数据。
例 2,float b[5][3];
定义了一个 5?3的数组 b,即数组为 5行 3列,可存放 15个实型数据。
数组元素的引用,
在二维数组中,一个元素的位置由所在的行和列决定一般形式:
数组名 [行号 ][列号 ]
注意:二维数组的行号、列号也是从 0开始的。
例如,对数组 int a[2][3];其 6 个元素是:
第 (0 )行,a[0][0],a[0][1],a[0][2]
第 (1 )行,a[1][0],a[1][1],a[1][2]
对数组 float b[5][3];其 15个元素是:
第 (0 )行,a[0][0],a[0][1],a[0][2]
第 (1 )行,a[1][0],a[1][1],a[1][2]
第 (2 )行,a[2][0],a[2][1],a[2][2]
第 (3 )行,a[3][0],a[3][1],a[3][2]
第 (4 )行,a[4][0],a[4][1],a[4][2]
二维数组的顺序存储结构(按行存放):
int a[2][3]; float b[3][2];
a[0][0]
a[0][1]
a[0][2]
a[1][0]
a[1][1]
a[1][2]


行第


B[0][0]
b[0][1]
b[1][0]
b[1][1]
b[2][0]
b[2][1]
第0行第1行第2行输入、输出:二层循环
1 2 3 4 for(i=0;i<4;i++) /*输入 */
10 1 2 3 for(j=0;j<4;j++)
10 10 1 2 scanf(“%d”,&a[i][j]);
10 10 10 1
for(i=0;i<4;i++)
{ for(j=0;j<4;j++) /*输出第 i行 */
printf(“%4d”,a[i][j]);
printf(“\n”); /* 换行 */
}
赋值操作:
j
i 1 2 3 4 上三角,i<j (蓝色)
10 1 2 3 对角线,i==j
10 10 1 2 下三角,i>j ( 红色)
10 10 10 1
for(i=0;i<4;i++)
for(j=0;j<4;j++)
if(i<=j) a[i][j]=1+j-i; /*上三角和 对角线 */
else a[i][j]=10; /*下三角 */
例:从键盘上输入 9个整数,保存在二维数组中,按数组原来位置输出第一行和第一列的所有元素。
1 2 3
4 5 6
7 8 9
第0行第1行第3行第

列第

列第

列 2
4 5 6

运行结果分析,1、输入数组。 2、输出数组要考虑不是所有数据都输出。
思考:应该输出的数据在位置关系上有何特点?(关键!)
main()
{int i,j,a[3][3];
for(i=0;i<3;i++)
for(j=0;j<3;j++)
scanf(“%d”,&a[i][j];
for(i=0;i<3;i++)
{for(j=0;j<3;j++)
if(i==1||j==1) printf(“%6d”,a[i][j]);
else printf(“,);
printf(“\n”);
}
}
2、二维数组的初始化
⒈分行给二维数组赋初值,如:
static int a[3]4]={{1,2,3,4},{5,6,7,8},{9,10,11,12}};
1 2 3 4
5 6 7 8
9 10 11 12
⒉ 可以按数组排列的顺序将所有数据写在一个花括号内,
如:
static int a[3]4]={1,2,3,4,5,6,7,8,9,10,11,12};
⒊ 可以对部分元素赋初值,如:
static int a[3][4]={{1},{0,6},{0,0,11}};
1 0 0 0
0 6 0 0
0 0 11 0
⒋ 如果对全部元素都赋初值,则定义数组时可以不指定第一维的长度,如:
static int a[ ][4]={1,2,3,4,5,6,7,8,9,10,11,12};
5.可以缺行对部分元素赋初值如:
static int a[3][4]={{1},{5,6}};
1 0 0 0
5 6 0 0
0 0 0 0
6.,,可以隔行对部分元素赋初值如:
static int a[3][4]={{1},{},{9}};
1 0 0 0
0 0 0 0
9 0 0 0
例:求矩阵的转置矩阵。
1 2 3 1 4 7
4 5 6 2 5 8
7 8 9 3 6 9
什么是转置矩阵:
从上可知,
方法 1:转置矩阵是将原矩阵元素按行列互换形成的。
1 2 3 1 4 7
4 5 6 2 5 8
7 8 9 3 6 9
方法 2:沿主对角线将对称位置互换元素即可。这时可将转置矩阵放在原数组中。
主对角线 i==j
副对角线 i+j=2
方法 1之 程序:
#include <stdio.h>
main()
{int i,j;
int a[3][3]={1,2,3,4,5,6,7,8,9},b[3][3];
for (i=0;i<3;i++)
for(j=0;j<3;j++) b[i][j]=a[j][i];
for(i=0;i<3;i++)
{for(j=0;j<3;j++)
printf(“%6d”,b[i][j]);
printf(“\n”);
}
}
方法 2之程序留给同学们作思考题!
7.3 多维 数 组
1,多维数组的定义与存储多维数组的一般说明形式为:
类型名 数组名 [常量表达式 1][ 常量表达式 2],… ;
和一维数组、二维数组一样,只能对数组的各个元素进行操作,数组元素也是由下标来区分的。
由于大量占有内存的关系,三维和三维以上数组较少使用。因此,这里只介绍三维数组。
int a[2][3][4];
定义了一个三维数组,存储结构如下图所示。可以把它理解为具有两个(层) 3行 4
列的二维数组组成。
a[0][1][2] a[0][1][3] a[0][2][0] a[0][2][1] a[0][2][2] a[0][2][3]
a[0][0][0] a[0][0][1] a[0][0][2] a[0][0][3] a[0][1][0] a[0][1][1]
a[1][0][0] a[1][0][1] a[1][0][2] a[1][0][3] a[1][1][0] a[1][1][1]
a[1][1][2] a[1][1][3] a[1][2][0] a[1][2][1] a[1][2][2] a[1][2][3]
2、多维数组的初始化多维数组的初始化和二维数组一样,遵守同样的规则。
如果给出全部元素的初值,第一维的下标个数可以不用显示说明。例如:
int a[ ][2][3]={1,2,3,4,5,6,7,8,9,10,11,12};
多维数组的初始化可以按第一维下标进行分组,使用括号将每一组数据括起来。例如:
int a[ ][2][3]={{1,2,3,4,5,6},{7,8,9,10,11,12}};
也可以写为:
int a[2][2][3]={{1,2,3,4,5,6},{7,8,9,10,11,12}};
但是,不能写成:
int a[2][2][3]={{1,2,3},{4,5,6},{7,8,9},{10,11,12}};/*
编译通不过*/
如果写成:
int a[ ][2][3]={{1,2,3},{4,5,6},{7,8,9},{10,11,12}};/*
结果不对*/
例,一个三维数组的赋值与输出程序如下,
#include<stdio.h>
main()
{
int i,j,k,a[][2][3]={1,2,3,4,5,6,7,8,9,10,11,12};
for(i=0;i<2;i++)
{ for(j=0;j<2;j++)
{ for(k=0;k<3;k++)
printf("%3d",a[i][j][k]);
}
printf("\n");
}
}
输出结果为,1 2 3 4 5 6
7 8 9 10 11 12
7.4 字 符 串 与 字 符 数 组
1、基本概念:
字符数组:元素类型为字符类型的数组。
字符串:若干有效字符的序列;
可包含转义字符,ASCⅡ 码表中的字符;
形式为:用双引号括起来的字符序列;
例:,I am a student.”
字符串的结束标志,‘ \0’。
注,C语言中无字符串变量,字符 串的 存储完全依赖于字符数组中的。
2,字符数组的初始化
① 在定义字符数组时进行初始化
char ch[7]={?s?,?t?,?u?,?d?,?e?,?n?,?t?};
② 在对全部元素指定初值时,可省写数组长度 。
char ch[]={?s?,?t?,?u?,?d?,?e?,?n?,?t?};
③ 可直接用字符串初始化字符数组
char ch[]={“student”};
或,char ch[]=“student”;
而,char ch[7]=“student”; 是错误的 。
但这时数组长度为 8,系统自动在字符串尾部补上字符串结束标志 ‘ \0?.
s t u d e n t \0
3、字符串的输入
( A)使用 scanf函数输入字符串例,char st[15];
sacnf(“%s”,st);
但,scanf(“%s”,&st);是错误的;
因为 st就代表了该字符数组的首地址。
注:输入时,以回车或空格作为结束标志;
即用 scanf输入的字符串中不能含有空格。
( B)使用函数 gets()输入字符例,char st[15];gets(st);
注:输入时,以回车作为结束标志。可含空格。
4、字符串的输出
( A)用 printf函数例:
char st[15]=“1234567”;
printf(“st=%s,%c,%c”,st,st[3],st[6]);
注:用 printf输出字符串时,要用格式符,%s”,输出时从数组的第一个字符开始逐个字符输出,直到遇到第一个‘ \0?为止

( B)用 puts函数例,puts(ch);
将字符数组中包含的字符串输出,然后再输出一个换行符。因此,用 puts()输出一行,不必另加换行符。
改为:,1234\0567”
5,字符处理函数库函数原型在 string.h中。
( 1) 字符串拷贝函数,strcpy()
调用格式,strcpy(str1,str2);
功能:将原字符串 str2复制到目标字符数组 str1中。
说明,str1的长度应不小于 str2的长度。
str1必须写成数组名形式。
str2可是字符串常量或字符数组名形式。
例,char s1[10],s2[8]=“student”;
strcpy(s1,s2);
注意:不能直接使用赋值语句来实现拷贝或赋值。
例,s1=s2;
s1=“student” 都是错误的
( 2) 字符串连接函数 strcat()
调用格式,strcat(str1,str2);
功能:将 str2连同 ‘ \0?连接到 str1的最后一个字符
( 非 ‘ \0?字符 ) 后面 。 结果放在 str1中 。
例:
char s1[14]=“I am a,};
char s2[5]=“boy.”;
strcat(s1,s2);
连接前,s1:
s2:
连接后,s1
I a m a \0
b o y,\0
I a m a b o y,\0
(3)字符串比较函数 strcmp()
调用格式,strcmp(str1,str2);
功能:若 str1=str2,则函数返回值为 0;
若 str1>str2,则函数返回值为正整数;
若 str1<str2,则函数值返回为负整数。
比较规则:
● 两个字符串自左至右逐个字符比较,直到出现不同字符或遇到 ‘ \0?为止。
● 如字符全部相同,则两个字符相等;
若出现不同字符,则遇到的第一对不同字符的 ASCⅡ 大者为大。
例,if (strcmp(str1,str2)==0){… };
而,if(str1==str2){… };是错误的。
( 4)字符长度函数 strlen()
调用格式,strlen(字符串);
功能:求字符串的实际长度即所含字符个数(不包括‘ \0?)。
例,char str[15]=“Niagara Falls”;
int length;
length=strlen(str); (=13)
strlen(“very\0good”); (=4)
(尼亚加拉瀑布)
例:输入一行字符,统计其中有多少个单词,单词之间用空格隔开 。
#include <stdio.h>
main(0
{char c,str[81];int i,num=0,word=0;
gets(str);
for(i=0;(c=sting[i])!=?\0?;i++)
if(c==)word=0;
else if(word==0)
{ word=1;
num++;}
printf(,%d words in the line.\n”,num);
}
7.5 字 符 串 数 组
所谓字符串数组就是数组中的每个元素又都是一个存放字符串的数组。利用 C语言中数据构造的特点很容易实现这一数据结构。
在 C语言中,可以把一个二维数组看成是一个一维数组,每个元素又是包含有若干个元素的一维数组。从这一概念出发,可以将一个 二维字符数组视为一个字符串数组 。
char name[3][10];
name可以看成有 3个元素每个元素可以存放 10个字符的数组,作为串使用时,最多可以存放 9个有效字符,最后一个存储单元留给‘ \0?。 因此,可以认为:二维字符数组的第一个下标决定了字符串的个数 ; 第二个下标决定了字符串的最大长度 。所以把它看成一个字符串数组。
字符串数组的存储结构字符串数组也可以在定义的同时赋初值。例如:
char name[3][10]={“zhangsan”,,lisi”,,wangwu” };
各元素的存储情况如下图所示:
z h a n g s a n \0
l i s i \0
w a n g w u \0
name[0]
name[1]
name[2]
7.6 数 组 作 为 函 数 参 数数组作为函数的参数应用非常广 泛。
1,数组元素做函数实参数组元素的使用与变量相同,作为函数参数也与变量相同,是单向的按值传递。
例:从键盘上输入两个字符串,比较两者的大小。
#include <stdio.h>
#include <string.h>
#include <process.h>
main()
{int i,flag;
char str1[80];str2[80];
gets(str1);
gets(str2};
if((strlen(str1)==0)||(strlen(str2)==0))
{ printf(“Input error!”);
exit(0);}
i=0;
do {
flag=comps(str1[i],str[i]);
i++;
}while ((str1[i]!=?\0?)&&(str2[i]!=?\0?)&&(flag==0);
if(flag==0) printf(“(“%s=%s”,str1,str2”);
else if (flag>0) printf(“%s>%s”,str1,str2);
else printf(,%s<%s”,str1,str2);
}
comps(c1,c2)
char c1,c2;
{ int val;
val=c1-c2;
return(val);
}
2、数组名作函数参数
★ 数组名作函数参数时形参与实参都应使用数组名,且分别在被调用函数与主调函数中的说明。
★ 实参与形参类型要一致。
★ 实参数组与形参数组大小可以不一致。 C编译程序不检查形参数的大小。
★ 形参数组可不指定大小。( 1)、在一维形参数组名后面可只根一对空方括号。为在被调用函数中处理数组元素的需要,可另设一参数来传递数组元素个数。( 2)、对多维数组而言,形参的第一维可不指定,但其它维必须指定。
★ 数组名做函数参数时是把实参数组的 起始地址 传给了形参数组
,即,形参数组与实参数组对应同一段内存单元 。 利用这个特点,可利用数组返回多个值。
例:求数组元素的最大值与最小值,并把它们分别放在第一、第二个元素中 。
#include <stdio.h>
maxmin(b)
int[][4];
{int i,j,max,min;
max=min=b[0][0];
for(i=0;i<3;i++)
for(j=0;j<4;j++)
if (b[i][j]>max)max=b[i][j];
else if(b[i][j]<min) min=b[i][j];
b[0][0]=max;b[0][1]=min;
return(max);}
main()
{int a[3][4]={1,0,-32,21,10,4,
4,4,345,2,12,0];
printf(“max=%d \n”,maxmin(a));
printf(“max=%d”,a[0][0]);
printf(“min=%d”,a[0][1]);
}
例:用冒泡法将 10 个整数按从小到大的顺序排序。
冒泡排序的基本思想:将相邻两数进行比较,若前面的大,则两数交换,直到最后一个元素。这样,最大的数就到了最后面。完成了一趟的冒泡排序。一趟排序,使得一个数到了其最终位置。则 n-1趟的排序使得 n-1个数有序,也即 n个数据的有序。
例,int a[10]; (n=10)
第一趟:若 a[0]>a[1],则 a[0] a[1]
a[1]>a[2],则 a[1] a[2]
...
a[8]>a[9],则 a[8] a[9]
经过 9( n-1)次比较,a[9]中存放的是 a[0]到 a[9]中的最大值第二趟:
范围,a[0]--a[8];
比较次数,8(n-2)
结果,a[8]有序。
类推,?
main()
{int a[10];
int i,m;
void sort(int b[],int k);
void print(int b[],int k);
printf(“\nInput m:”);
scanf(“%d”,&m);
for(i=0;i<m;i++)
scanf(“%d”,&a[i]);
sort(a,m);
print(a,m);
}
void sort(int b[],int k)
{ int i,j,t,flag;
for(j=0;j<k-1;j++)
{ flag=0;
for(i=0;i<k-j-1;i++)
if(b[i]>b[i+1])
{ t=b[i],b[i]=b[i+1],b[i+1]=t;
flag=1;
}
if (flag==0)break;
}
}
void print(int b[],int k)
{ int i;
for(i=0;i<k;i++)
{
if(i%8==0)puts(,\n”);
printf(“%6d”,b[i]);
}
}