数组结构
前面介绍的数据类型都是基本数据类型,例如整型、字符型、浮点型等数据,这些都是简单的数据类型。对于有些数据,只有简单的数据类型是不够的,难以反映出数据的特点,也难以有效地进行处理。
例如:假设需要接收并存储100个学员的成绩,此时无法使用for循环依次读取每个学员的成绩,因为在for循环中只通过一个变量依次存储这些值,这样,for循环运行结束后只能保存最后一个学员的成绩,而前面99个学员的成绩都无法找回。因此,在这种情况下,只能通过声明100个变量,并且编写100个nextFloat()和printf()语句,来接收和输出用户输入的学员成绩。那么,如果要求处理5000个学员的成绩呢,岂不要定义5000个存储成绩的变量?
日常生活中有大量的数据类似于此,它们有相同的类型,需要的处理方法也一致,为了实现对这些数据统一表达和处理,C语言提供了“数组”这一数据结构,数组可以把同一类型的数据有序进行排列,进行同一存储和操作。数组结构如下图所示;

数组是有序数据的集合,其结构的最大特征就是通过有序编号固定集合内每个数据的位置(数组结构如上图所示)。有序编号被称为下标,被固定的每个数据被称为元素。数组内所包含元素的数据类型没有限制,可以是任意类型的数据,如整数、字符、浮点数等。下标值是一个从0开始的连续正整数。上图的0、1、2、length-2、length-1为数组元素的下标,其中length是数组包含元素的个数,length-2为数组倒数第二个元素,length-1为数组最后一个元素。程序可以通过下标访问数组中的元素。
下图显示了数组中涉及的概念,包括数组的名称、大小、数组中的元素及数组的下标。

数组是C语言中一种非常重要的数据类型,是具有相同的数据类型且按一定次序排列的一组变量的集合体,构成一个数组的这些变量称为数组元素。数组有一个统一的名称,通过名称和数组下标可以访问数组中的任意元素。
生活中有很多类似数组应用的例子,例如:一个足球队通常会有几十个人,球迷会把他们看作是所喜爱球队的成员,然后再利用他们的号码来区分每一个队员,这时候,球队就是一个数组,而号码就是数组的下标,球迷可以用某某队某某号来确定每一个球员。放到程序当中,球队就是数组名称,球员号码就是下标。
数组结构的维数
前面介绍的数组结构都是多行单列数据结构,行数为数组的长度,列为数组中的元素。如前面介绍的Rate数组有四行一列,因此其数组长度为4,列元素分别为1.5、3.2、0.09、36.5。
多行单列的数组结构也可以用表格来表示,上图的数组结构就可以用1行4列的表格来表示:
| 1.5 | 3.2 | 0.09 | 36.5 |
类似于上图的数据结构,我们称之为一维数组,一维数组中只有一行数据,数据可以通过下标获取,下标为0的是1.5,下标为1的是3.2,下标为2的是0.09,下标为3的是36.5。
在用程序解决实际问题中,经常会遇到类似下面问题。例如,有50名学员,现在需要存储每名学员的序号和两门课程的成绩。数据结构可用如下表格所示:

表格结构为50行3列结构,要存储类似上面表格结构的数据,用前面介绍的一维数组不能满足存储要求。因为一维数组只能存储1行数据,现在有50行数据。用什么数据结构存储上述表格的数据呢?试想一下,如果一维数组的每个元素本身也是一维数组,就可以存储上述表格数据,数据结构如下图所示:

二维数组数据结构
上图所示的数据结构中,Students为数组的名称,数组的长度为50,其数组元素本身也是一个长度为3的数组,该数组有三个元素,分别对应学员序号、语文成绩、数学成绩,其中下标0对应学员序号,下标1对应语文成绩,下标2对应数学成绩。这样就可以根据第一个数组和第二个数组的下标访问一个学员的学号、语文、成绩。例如:如果需要访问学员2的数学成绩,可以通过Students[1][2]来访问学员2的数学成绩,Students[1][2]是访问数组的语法,后面一节会有讲述;再如,如果需要访问学员50的语文成绩,可以通过Students[49][1]来访问学员50的语文成绩。
类似于上面的数据结构,我们称之为二维数组,二维数组本质上是以数组作为数组元素的数组,访问二维数组的元素时,需要同时给出第一个数组的小标和第二个数组的下标。
进一步考虑,在上述例子中,如果学员的语文和数学成绩按学期来进行存储,其数据结构可用如下表格所示:

上面的表格结构为50行3列结构,但在语文和数学列单元中又嵌套了一个3行1列的子表,二维数组无法存储上面的表格数据。如果把语文和数学列单元的数据也用一个数组表示,就可以满足上述表格数据存储要求了,其数据结构如下图所示:

上图所示的数据结构中,Students为数组的名称,数组的长度为n,其数组元素本身也是一个长度为3的数组,该数组有三个元素,分别对应学员序号、语文成绩、数学成绩,其中语文成绩和数学成绩元素本是也是一个数组,该数组长度为n,记录了n个学期的考试成绩。这样就可以根据第一个数组、第二个数组的下标访问一个学员的学号,根据第一个数组、第二个数组和第三个数组的下标访问学员的学期成绩。例如:如果需要访问第一个学员的学员学号,可以通过Students[0][0]来访问第一个学员的学员学号;访问第一个学员语文第一学期的成绩,可以通过Students[0][1][0]来访问;访问第一个学员语文第n学期的成绩,可以通过Students[0][1][n]来访问。
类似于上面存储语文和数学学期成绩的数据结构,我们称之为三维数组,三维数组本质上是三层数组的嵌套,分为第一层数组、第二层数组、第三层数组。第一层数组的元素是第二层数组,第二层数组的元素是第三层数组。访问三维数组的元素时,需要同时给出三个数组的下标。
可以类推,数组的数据结构可有四维数组、甚至更多维数组,随着数组维数的增多,其数据存储复杂度和计算复杂度也相应提升。在编程应用中,一般常用的为一维数组、二维数组和三维数组。
对数组的访问一般是通过下标进行的。在一维数组中,数组的下标是由一个数字构成的,通过这一个数字组成的下标对数组的内容进行访问;在二维数组中,数组的下标是由两个数字构成的,通过这两个数字组成的下标对数组的内容进行访问;在三维数组中,数组的下标是由三个数字构成的,通过这三个数字组成的下标对数组的内容进行访问。
一维数组的实现
前面讨论过一维数组的概念,一维数组只有一个下标。
声明一维数组
在C语言中,声明一维数组的语法为:
类型说明符 数组名[常量表达式];
其中类型说明符可以是C语言中任何有效的数据类型,如int、float、char等。数组名是引用数组的名称,数组名的命名需符合变量命名规则。常量表达式可以是大于零的整数常量,也可以是表达式,表达式计算结果必须返回大于零的整数。
例如:
int a[6];
//定义了一个数组,名称为a,数组中的元素类型是int,数组大小为6个元素。
float score[3+3];
//定义了一个数组,名称为float,数组中的元素类型是float,数组大小为5个元素。
一维数组的初始化
声明了一个数组,数组元素的值是随机数,要使用数组还需要对数组元素进行初始化,数组元素初始化后才能使用。
数组的初始化有两种方法,一种是静态初始化,另一种是动态赋值。
① 静态初始化
在声明数组时,直接给数组的每个元素赋上一个初始值。静态初始化一般在数组元素比较少时使用。
静态初始化的语法为:
数组类型 数组名[常量表达式]={值1, 值2,……, 值n};
或为:
数组类型 数组名[]={值1, 值2,……, 值n};
初始化时若不指定数组元素,数组元素的长度以初始化的元素个数为准。若指定了元素长度,初始化元素的个数不能大于指定的数组长度。
例如:
int a[] = {26,67,89,106,210};
float score[5] = {87.5,67.2,96.8,92.6,75.9};
char chArray[] = {‘a’,’m’,’o’,’p’,’q’};
对于数组a,可以看出数组中有5个元素,分别是a[0]、a[1]、a[2]、a[3]、a[4]、a[5],即数组下标从0开始,这5个元素的数值依次为a[0]=26、a[1]=67、a[2]=89、a[3]=106、a[4]=210。其在内存中结构如图6-5所示。

图 a数组在内存中的存储结构
错误的初始化:
int a[];
a[] = {26,67,89,106,210};
数组的初始化只能在声明中完成,不能声明完成后,再进行初始化,数组的大小由初始化的元素决定。
② 数组的动态赋值
有时,数组并不需要在声明时就赋初值,而是在使用时才进行赋值。另外,有些数组比较大,元素非常多,用静态初始化不方便,这样就需要使用动态赋值。
●先声明数组
例如:
int a[10];
float = b[5];
a[0] = 20;
b[1] = 3.14f;
一维数组元素的访问
使用数组的最基本操作是数组元素的访问,对数组的使用最终都通过对元素的使用而实现。数组的元素通过数组下标进行访问。前面已经讲过,在C语言中,数组下标是从0开始的,所以包含n个元素的数组下标的范围是0到n-1。例如:前面定义的数组a,其下标范围为0到4。要访问a数组的元素,可通过a[0]、a[1]、a[2]、a[3]、a[4]访问a数组的各个元素,此时,方括号中的数字是数组的下标,表示访问数组的第几个元素。
下面通过一个完整的示例,演示如何声明一个数组,并为数组动态赋值。
例1:5件商品以内的价格小计
编程要求:要求用户输入5件商品的价格,并循环输出商品的价格,最后输出商品的累计价格。
程序清单 sample.c
int main()
{
//声明存储商品总价的变量,并初始化为0.0
float price[5]={0.0f,0.0f,0.0f,0.0f,0.0f};
// 商品累计价格
float total = 0.0f;
//count为循环次数
int count = 5;
//i为循环变量
int i;
//循环接收用户输入的5个商品价格
for( i=0; i < count; i++ )
{
printf("请输入 %d 个商品的价格: ",i+1);
//用户输入的第i件商品价格赋值给price数组的第i个元素
scanf("%f",&price[i]);
total += price[i];
printf("\n");
}
printf("*****商品小计******\n");
//循环输出商品的价格:
for( i=0; i < count; i++ )
{
printf("第 %d 件商品价格为:%.2f;\n",i+1,price[i]);
}
printf("商品价格合计为: %.2f",total);
}
二维数组的实现
前面介绍了一维数组。在实际问题中有些数据信息是二维的或者多维的。多维数组元素有多个下标,以标识它在数组中的位置。本节只介绍二维数组,多维数组可由二维数组类推而得到。
声明二维数组
二维数组的声明与一维数组类似,只是需要给出两对方括号,声明二维数组的语法为:
类型说明符 数组名[][];
例如:
int a[3][2];
score[2+1]1+1];
其中,声明语句的第一个方括号可以称为行数组,第二个方括号可以称为列数组。
二维数组的初始化
同一维数组类似,二维数组在使用前也要进行初始化。初始化也分为静态初始化和动态赋值。
① 静态初始化
静态初始化是在数组声明时,对声明的数组元素赋初值的过程。
例如:
int array[][] = {{3,-9,6},{8,0,1},{11,9,8}};
声明并初始化数组array,它有3个元素,每个元素又都是有3个元素的一维数组。外层大括号对里面的元素是行数组的元素,因为行数组元素是一维数组,因此每个行数组元素也用大括号对包括起来,里面的元素是列数组的元素。
另外,用指定初值的方式对数组进行初始化时,列数组元素的个数可以不同。
例如:
int array[][] = {{3,6},{8,0,1},{11,9,8}};
此时,第一个列数组的元素个数是2,第二个和第三个列数组的元素个数是3个。
② 动态赋值
二维数组的动态赋值和一维数组的动态赋值操作相同。
●先声明二维数组
例如:
int b[1][2];
int a[3][2];
●动态赋值
a[0][0] = 3;
a[0][1] = 6;
b[0][0] = 5;
b[1][0] = 10;
b[2][0] = 15;
二维数组元素的访问
二维数组的元素通过两个下标进行访问,分别是行数组下标和列数组下标,例如:对于二维数组a,可通过a[i][j]进行访问,其中i和j为数组a的下标。
例如:
#include <stdio.h>
#include <stdio.h>
int main()
{
int a[3][3] = {{3,-9,6},{8,0,1},{11,9,8}};
int i,j;
for( i = 0; i < 3; i++ )
{
for(j=0; j<3;j++)
printf("a[%d][%d] 数组元素的值为: %d;\n",i,j,a[i][j]);
}
}
在遍历数组的外层for循环中,循环条件为a数组第1维的长度,在内层for循环中,循环条件为a数组第2维的长度。
二维数组的存储结构
前面array数组的存储结构如下图所示:

图 array数组存储结构图
下面通过一个完整的示例,演示如何声明一个二维数组,赋值并输出。
例2:二维数组练习
编程要求:使用一个二维数组,存储5名同学的语文成绩和数学成绩,成绩由用户录入,最后输出这5名同学的语文成绩和数学成绩。
程序清单 sample.c
void main()
{
/**
* 声明grade数组
* 数组第1维是学生元素
* 数组第2维是成绩元素
*/
float grade[5][2];
//i,j为循环变量
int i;
//循环接收用户输入的学生成绩
for( i=0; i < 5; i++ )
{
printf("请输入 %d 个学生的语文成绩: ",i+1);
scanf("%f",&grade[i][0]);
printf("请输入 %d 个学生的数学成绩: ",i+1);
scanf("%f",&grade[i][1]);
printf("\n");
}
printf("*****学生成绩******");
//输出学生成绩
for( i=0; i < 5; i++ )
{
printf("语文成绩:%.2f 数学成绩:%.2f \n",grade[i][0],grade[i][1]);
}
}
程序功能主要是演示二维数组声明、初始化、动态赋值、输出数组元素的使用方法。
程序声明一个二维数组grade,其中第1维有5个元素,存储5名学生的信息,第2维数组有2个元素,分别存储学生的语文成绩和数学成绩。程序通过循环依次获取用户输入的5位学生的语文成绩和数学成绩,并存储到二维数组中。最后,循环输出每位学生的语文成绩和数学成绩。