第5章 数组和指针 5.1 一维数组的定义和引用 为了处理方便,把具有相同类型的若干变量按有序的形式组织起来,这些按序排列的 同类变量的集合称为数组。在C语言中,数组属于构造数据类型。一个数组可以分解为 多个数组元素(简称为元素),这些数组元素可以是基本数据类型或构造类型。因此按数 组元素的类型不同,数组又可分为数值数组、字符数组、指针数组等各种类别。 对于序列a0,a1,a2,…,an-1,在C语言中就用数组来表示,a 为数组名,下标代表数组 中元素的序号,数组元素也称为下标变量,ai 用a[i]表示,i 就为下标,一维数组是最简单的 数组,它的元素只有一个下标,如a[8],此外还有二维数组(元素有二个下标,如a[2][3]), 三维数组(元素有3个下标,如a[2][3][6])和多维数组(元素有多个下标),它们的概念和使 用方法相似,本节先介绍一维数组。 5.1.1 定义一维数组 定义一维数组的格式如下: 数组元素类型数组名[常量表达式],…; 其中,数组元素类型可以是任意一种基本数据类型或构造数据类型,数组名是用户定义的 数组标识符。“[”和“]”之间常量表达式表示数组元素的个数,也称为数组的长度。 例如: int a[18]; /*定义整型数组a,有18 个元素*/ float b[16],c[28]; /*定义实型数组b,有16 个元素,实型数组c,有28 个元素*/ char ch[29]; /*定义字符数组ch,有29 个元素*/ 对于数组的定义应注意以下几点。 (1)对于同一个数组,其所有元素的类型都相同。 (2)数组名不能与其他变量名相同,例如: int main(void) /*主函数main()*/ { int a; /*定义变量a*/ float a[10]; /*定义数组*/ … ·121· } 是错误的。 (3)“[”和“]”之间的常量表达式表示数组元素的个数,如a[18]表示数组a有18个 元素。但是其下标从0开始计算。因此18个元素分别为a[0],a[1],a[2],…,a[17]。 (4)不能在“[”和“]”之间用变量来表示元素的个数,但是可以是常量或常量表达式。 例如: #define FD 5 /*定义常量FD*/ int main(void) /*主函数main()*/ { int a[3+2],b[7+FD]; /*定义数组a、b*/ … } 是合法的。但是下面的使用方式是错误的: int main(void) /*主函数main()*/ { int n=5; /*定义整型变量n*/ int a[n]; /*定义数组,错,元素个数n 不是常量表达式*/ … } (5)在同一个类型的定义中,可以同时定义多个变量和多个数组。例如: int a,b,c,d,k1[10],k2[20]; /*定义多个变量与多个数组*/ 5.1.2 引用一维数组的元素 数组元素是组成数组的基本单元。实际上,数组元素也是一种变量,其标识方法为数 组名后跟一个下标。下标表示了元素在数组中的顺序号。引用一维数组元素的一般格式 如下: 数组名[下标] 其中,下标只能为整型常量、整型变量或整型表达式。例如,a[6]、a[i+j]、a[k]都是合法 的数组元素。数组元素通常也称为下标变量。在C语言中只能逐个地使用下标变量,而 不能一次引用整个数组。例如,输出有10个元素的数组必须使用循环语句逐个输出各下 标变量: for (i=0;i<10;i++) printf("%d",a[i]); /*循环输出各数组元素*/ 而不能用一个语句输出整个数组,下面的写法是错误的: printf("%d",a); /*错,不能用一个语句输出整个数组*/ ·122· 例5.1 一维数组示例,程序演示见5011.mp4~5013.mp4。 /*文件路径名:e5_1\main.c*/ #include<stdio.h> /*标准输入输出头文件*/ int main(void) /*主函数main()*/ { int i,a[10]; /*定义变量i 与数组a*/ for (i=0;i<10;i++) a[i]=2*i+1; /*循环为数组元素赋值*/ for (i=9;i>=0;i--) printf("%d",a[i]); /*循环逆序输出数组元素*/ printf("\n"); /*换行*/ return 0; /*返回值0,返回操作系统*/ } 程序运行时屏幕输出如下: 19 17 15 13 11 9 7 5 3 1 本例中用一个循环语句给a数组各元素存入奇数值,然后用第二个循环语句按逆序 从大到小输出各个奇数。 给数组赋值的方法除了用赋值语句对数组元素逐个赋值外,还可采用初始化赋值和 动态赋值的方法。数组初始化赋值是指在数组定义时给数组元素赋予初值。数组初始化 是在编译阶段进行的。这样将减少运行时间,提高效率。一维数组初始化赋值的一般格 式如下: 数组元素类型数组名[常量表达式]={值1,值2,…,}; 在“{”和“}”中的各数据值即为各元素的初值,各值之间用“,”间隔。例如: int a[10]={0,1,2,3,4,5,6,7,8,9 }; /*相当于a[0]=0;a[1]=1;…;a[9]=9*/ C语言对一维数组的初始赋值还有以下几点规定。 (1)可以只给部分元素赋初值。当“{”和“}”中值的个数少于元素个数时,只给前面 部分元素赋值,后面的元素自动赋值为0,例如: int a[10]={0,1,2}; /*表示给a[0]~a[2]共3 个元素赋值,而后7 个元素自动赋0 值*/ (2)只能给元素逐个赋值,不能给数组整体赋值。例如,给10个元素全部赋1值,只 ·123· 能写为 int a[10]={1,1,1,1,1,1,1,1,1,1}; /*10 元素初始化为1*/ 而不能写为 int a[10]=1; (3)如给全部元素赋值,则在数组定义中,可以不给出数组元素的个数。例如: int a[5]={1,2,3,4,5}; /*初始化5 个数组元素值*/ 可写为 int a[]={1,2,3,4,5}; /*初始化5 个数组元素值,省略元素个数*/ 可以在程序执行过程中,对数组作动态赋值。这时可用循环语句配合scanf()函数逐 个对数组元素赋值。 例5.2 编程求数组元素最大值,程序演示见5021.mp4~5023.mp4。 /*文件路径名:e5_2\main.c*/ #pragma warning(disable:4996) /*禁止对代号为4996 的警告*/ #include<stdio.h> /*标准输入输出头文件*/ int main(void) /*主函数main()*/ { int i,max,a[10]; /*定义变量与数组*/ printf("输入10 个数:"); /*输入提示*/ for (i=0;i<10;i++) scanf("%d",&a[i]); /*循环输入数组元素*/ max=a[0]; /*设a[0]最大*/ for (i=1;i<10;i++) if (a[i]>max) /*如果a[i]更大*/ max=a[i]; /*将a[i]赋值给max*/ printf("最大值:%d\n",max); /*输出最大值*/ return 0; /*返回值0,返回操作系统*/ } 程序运行时屏幕输出如下: 输入10 个数: 1 7 4 9 12 8 18 56 98 0 ·124· 最大值: 98 本例程序中第一个for语句逐个输入10个数到数组a中。然后把a[0]送入max中。 在第二个for语句中,a[1]~a[9]逐个与max中的值进行比较,若比max的值大,则把该 下标变量送入max中,因此max总是在已比较过的下标变量中为最大者的值。比较结 束,输出max的值。 5.2 二维数组 前面介绍了一维数组,在实际问题中有很多量是二维的或多维的,本节只介绍二维数 组,多维数组可由二维数组类推得到。 5.2.1 定义二维数组 定义二维数组一般形式如下: 数组元素类型数组名[常量表达式1][常量表达式2],…; 其中,常量表达式1表示第一维下标的长度,常量表达式2 表示第二维下标的长度。 例如: int a[3][4]; /*定义二维数组*/ 定义了一个3行4列的数组,数组名为a,其下标变量的类型为整型。该数组的下标变量 (元素)共有3×4个,即 a[0][0],a[0][1],a[0][2],a[0][3] a[1][0],a[1][1],a[1][2],a[1][3] a[2][0],a[2][1],a[2][2],a[2][3] 二维数组在概念上是二维的,即是说其下标在两个方向上变化,下标变量在数组中的 位置也处于一个平面之中,而不是像一维数组只是一个方向上变化。但是实际的硬件存 储器却是连续编址的,也就是存储器单元是按一维线性排列的。在一维存储器中存放二 维数组,可有两种方式:一种是按行排列,即存储完一行之后顺次存储下一行;另一种是 按列排列,即存储完一列之后再顺次放入下一列。在C语言中,二维数组是按行排列的。 对于上面定义的数组,按行顺次存放,先存储第1行,再存储第2行,最后存储第3行。每 行中有4个元素也是依次存储。具体存储顺序如下: a[0][0],a[0][1],a[0][2],a[0][3],a[1][0],a[1][1],a[1][2],a[1][3],a[2][0], a[2][1],a[2][2],a[2][3] 5.2.2 引用二维数组的元素 二维数组的元素也称为双下标变量,其引用形式如下: ·125· 数组名[下标1][下标2] 其中,下标1和下标2应为整型常量、整型变量或整型表达式。例如,a[3][4]表示a数 组3行4列的元素。引用下标变量与定义数组在形式中有些相似,但两者具有完全不同 的含义。定义数组的“[”和“]”之间给出的是某一维的长度;而数组元素中的下标是该元 素在数组中的位置标识。前者只能是常量,后者可以是常量、变量或表达式。 例5.3 一个学习小组有5个人,每个人有3门课的考试成绩如下所示。求全组分科 的平均成绩和各科总平均成绩。 姓名数学C语言数据库系统 张明80 75 92 王小艳68 78 88 李杰89 98 61 赵刚78 86 69 周勇98 96 89 可设一个二维数组a[5][3]存放5个人3门课的成绩。再设一个一维数组v[3]存放所 求得各分科平均成绩,设变量l为全组各科总平均成绩。程序演示见5031.mp4~5033.mp4, 具体编程如下: /*文件路径名:e5_3\main.c*/ #pragma warning(disable:4996) /*禁止对代号为4996 的警告*/ #include<stdio.h> /*标准输入输出头文件*/ int main(void) /*主函数main()*/ { int i,j,s=0,l,v[3]={0},a[5][3]; /*定义变量*/ printf("输入成绩:\n"); /*输入提示*/ for (i=0;i<5;i++) { /*行*/ for (j=0;j<3;j++) { /*列*/ scanf("%d",&a[i][j]); /*输入成绩*/ s=s+a[i][j]; /*求和*/ v[j]=v[j]+a[i][j]; /*对第j 门课程成绩求和*/ } } for (i=0;i<3;i++) ·126· v[i]=v[i]/5; /*求平均成绩*/ l=s/15; /*总平均成绩*/ printf("数学:%d\nC 语言:%d\n 数据库系统:%d\n",v[0],v[1],v[2]); /*输出各科平均成绩*/ printf("总平均成绩:%d\n",l); /*输出总平均成绩*/ return 0; /*返回值0,返回操作系统*/ } 程序运行时屏幕输出如下: 输入成绩: 80 75 92 68 78 88 89 98 61 78 86 69 98 96 89 数学:82 C 语言:86 数据库系统:79 总平均成绩:83 本例程序首先用了一个双重循环。在内循环中依次读入某学生3门课程的成绩,并 把这些成绩累加起来以及累加各门课程的成绩,退出内循环后再用一个循环求各门课程 的平均成绩,然后再求总平均成绩,最后按题意输出各成绩。 5.2.3 二维数组的初始化 二维数组初始化也是在数组定义时给各元素赋以初值。二维数组可按行分段赋值, 也可按行连续赋值。例如对数组a[5][3],按行分段赋值可写为 int a[5][3]={{80,75,92},{68,78,88},{89,98,61},{78,86,69},{98,96,89}}; /*按行分段赋值*/ 按行连续赋值可写为 int a[5][3]={80,75,92,68,78,88,89,98,61,78,86,69,98,96,89}; /*按行连续赋值*/ 这两种赋初值的结果是完全相同的。 例5.4 将例5.3的成绩通过初始化方式赋值,改写例5.3的程序,求全组各科的平均 成绩和各科总平均成绩,程序演示见5041.mp4~5043.mp4。 ·127· /*文件路径名:e5_4\main.c*/ #include<stdio.h> /*标准输入输出头文件*/ int main(void) /*主函数main()*/ { int i,j,s=0,l,v[3]={0}; /*定义变量*/ int a[5][3]={ /*按行分段赋值*/ {80,75,92}, {68,78,88}, {89,98,61}, {78,86,69}, {98,96,89}}; for (i=0;i<5;i++) { /*行*/ for (j=0;j<3;j++) { /*列*/ s=s+a[i][j]; /*求和*/ v[j]=v[j]+a[i][j]; /*对第j 门课程成绩求和*/ } } for (i=0;i<3;i++) v[i]=v[i]/5; /*求平均成绩*/ l=s/15; /*总平均成绩*/ printf("数学:%d\nC 语言:%d\n 数据库系统:%d\n",v[0],v[1],v[2]); /*输出各科平均成绩*/ printf("总平均成绩:%d\n",l); /*输出总平均成绩*/ return 0; /*返回值0,返回操作系统*/ } 程序运行时屏幕输出如下: 数学:82 C 语言:86 数据库系统:79 总平均成绩:83 对于二维数组初始化赋值有如下说明。 (1)可以只对部分元素赋初值,未赋初值的元素自动取0值。 例如: int a[3][3]={{1},{2},{3}}; /*只对部分元素赋初值,其他元素为0*/ ·128· 对每一行的第一列元素赋值,未赋值的元素取0值。赋值后各元素的值为 1 0 0 2 0 0 3 0 0 (2)如对全部元素赋初值,则第一维的长度可以不给出。 例如: 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}; /*对全部元素赋初值,可省略第一维的长度*/ 二维数组可以看作是由一维数组的嵌套而构成的。设一维数组的每个元素都是一个 一维数组,就组成了二维数组。一个二维数组可以分解为多个一维数组。二维数组a[3][4] 可分解为3个一维数组,其数组名分别为a[0]、a[1]、a[2]。对这3个一维数组无须再做 说明即可使用。这3个一维数组都有4个元素,例如一维数组a[0]的元素为a[0][0]、 a[0][1]、a[0][2]、a[0][3]。注意a[0]、a[1]、a[2]是数组名,不是一个单纯的下标变量。 5.3 用数组作为函数的参数 用一维数组名作为函数参数时,要求形参和相对应的实参都必须是元素类型相同的 数组,在函数形参表中,允许不给出形参数组的长度,或用一个变量来表示数组元素的 个数。 5.3.1 用数组元素作为函数的参数 由于函数实参可以是表达式,数组元素可以是表达式的组成部分,因此数组元素可以 作函数的实参,按普通变量的方式对数组元素进行处理。 例5.5 编写程序实现求给定的数组元素表示的工资中大于2000元的人数,程序演 示见5051.mp4~5053.mp4。 /*文件路径名:e5_5\main.c*/ #include<stdio.h> /*标准输入输出头文件*/ #define N 10 /*定义常量N*/ int main(void) /*主函数main()*/ { ·129· int wage[N]={1500,1890,2100,2600,1980,2980,3100,3600,2180,3208}; /*定义工资数组*/ int i,num=0; /*定义变量i,num*/ int Count2000(int w); /*函数声明*/ for (i=0;i<N;i++) num=num+Count2000(wage[i]); /*统计工资大于2000 元的人数*/ printf("共有%d 人的工资大于2000 元.\n",num); /*输出工资大于2000 元的人数*/ return 0; /*返回值0,返回操作系统*/ } int Count2000(int w) /*函数定义*/ { return w>2000 ?1:0; /*当w 大于2000 时,返回1,否则返回0*/ } 程序运行时屏幕输出如下: 共有7 人的工资大于2000 元. 本例程序中,Count2000(intw)用于判断w的值是否大于2000,当w>2000时,返回 1,否则返回0,在主函数main()中,扫描工资数组wage,对每个数组元素,调用 Count2000(),并累加返回值。 5.3.2 用一维数组名作为函数的参数 用数组名作为函数参数时,要求形参和相对应的实参都必须是元素类型相同的数组, 在函数形参表中,允许不给出形参数组的长度或用一个变量来表示数组元素的个数。 例5.6 数组a中存放了一个学生5门课程的成绩,求平均成绩,程序演示见5061 .mp4~5063.mp4。 /*文件路径名:e5_6\main.c*/ #include<stdio.h> /*标准输入输出头文件*/ float Average(float a[],int n) /*定义函数*/ { int i; /*定义整型变量*/ float av,s=0; /*定义实型变量*/ ·130·