目录
本篇博客主要介绍数组,先简单的说来一下数组,数组是用来存储和处理同一种数据类型数据的对象。使用数组可以为一组相同类型的变量起同一个名字,这样做可以在很大程度上减少代码的开发量,同时可为处理复杂问题提供解决的方法。
我们所使用的变量都有一个共同的特点,就是每个变量只能存储一个数值。如果多个变量属于同一种数据类型,就可以使用数组一起定义多个变量。数组表示的是一组数据类型相同的数,这组数当中的每一个元素都是一个独立的变量,数组就是用来存储和处理一组相同类型的数据的。
1、一维数组
一维数组是使用同一个数组名存储一组数据类型相同的数据,用索引或者下标区别数组中的不同元素。
1.1 一维数组的定义
一维数组定义的一般形式:
类型说明符 数组名[常量表达式];
例如:
int array[5];/*相对于这个例子来说,类型说明符: int ,数组名: array ,常量表达式:5。*/
或者:
#define MAX_LENGTH 5 /*数组的最大长度*/
int array[MAX_LENGTH];
上述两种形式都正确地定义了一个名称为 array 的整型数组,该数组含有 5 个整型变量,这5 个整型变量的下标依次是:array[0]、array[1]、array[2]、array[3]和 array[4]。
使用array数组存储数据,分别存储1、2、3、4、5。
说明:上图中数组 array 中的元素 array[0]是一个整型变量,它存储的是数据 1,它在使用上与一般的变量没有区别,比如 int x=1,array[0]与x 不同的地方只是 array[0]采用了数组名和下标组合的形式。
程序:
#include<stdio.h>
int main()
{
int array[5] = { 1,2,3,4,5 };/*定义一个数组并且对其进行初始化*/
printf("array[0]=%d array[1]=%d\n", array[0], array[1]);/*打印出array[0]、array[1]的数值*/
return 0;
}
或者输出全部的元素:
#include<stdio.h>
int main()
{
int array[5] = { 1,2,3,4,5 };/*定义一个数组并且对其进行初始化*/
int i = 0;
for (i = 0; i < 5; i++)
{
printf("array[%d] = %d\n",i,array[i]);/*打印出array数组的所有元素*/
}
return 0;
}
解释:从这两个例子可以看出使用数组的好处。很直观的一个好处就是很大程度上可以减少定义的变量的数目。原先需要定义 5 个变量,使用数组后,我们仅使用 array 作为数组名,改变下标值,就可以表示这些变量了。还有一个好处就是访问数组中的变量非常的方便,也只是变动下标就可以达到访问不同值的目的。当然还有其他一些好处,比如数据的查找、数据的移动等。
数组在内存中的存储形式如下图。
1.数组定义的说明:
(1)数组使用的是方括号 [ ],不要误写成小括号( )。
int array(10); /*是错误的形式*/
(2)对数组命名必须按照命名规则进行。
(3)数组下标总是从 0 开始的。以前面定义的 array 数组为例,数组元素下标的范围是从 0 到 4,而不是从 1 到 5,大于4的下标会产生数组溢出错误,下标更不能出现负数。
array[0] /*是存在的,可以正确访问*/
array[4] /*是存在的,可以正确访问*/
array[5] /*是不存在的,无效访问*/
array[-1] /*是错误的形式*/
(4) 定义数组时,array[5]中括号中的数字 5,表示的是定义数组中元素的总数。使用数组时array[2]=3中括号中的数值是下标,表示的是使用数组中的哪一个元素。
(5)在定义数组元素数目时,如上例中的 5 或者 MAX_LENGTH,用在数组的定义时,此处要求括号当中一定要是常量,而不能是变量。但是数组定义后,使用该数组的元素时,下标可以是常量也可以是变量,或者是表达式。如下面的代码就是错误的。
int number = 5;
int array[number]; /*在编译这样的代码时,编译器就会报错*/
2. 其他类型数组的定义
(1)整型数组的定义
int array[5];/*包含5个整型元素的数组名为 array 的数组,下标范围从 0 到 4 */
(2)浮点型数组的定义
float score[5];/*包含5个float类型元素的数组名为 score 的数组,下标范围从 0 到 4*/
(3)字符型数组的定义
char name[5];/*包含5个char类型元素的数组名为 name 的数组,下标范围 0 到 4 */
3.数组的地址
数组的一个很重要的特点是:它在内存中占据一块连续的存储区域。这个特点对于一维数组、二维数组、多维数组一样适用。前面例子中的数组 array 的存储区域,在某一个地址中存储了array[0]元素值 1,然后地址从低到高,每次增加 4 个字节(int 类型占用4个字节),顺序存储了其他数组元素的值。
假如我们现在已知 array[0]在内存中的地址,那么 array[1]的地址是多少呢?
array[1]就是在 array[0] 的地址基础上加4 个字节,同理,array[4]]的地址就是在 array[0]地址的基础上加 4个*4字节,共 16 个字节。所以对于数组,只要知道了数组的首地址,那么就可以根据偏移量计算出待求数组元素的地址。
注意:数组名就是首元素的地址。除非了以下两种情况:①sizeof(数组名)-----数组名表示整个数组。②&数组名-----数组名就是整个元素。除了这两种情况,其他时候数组名就是首元素的地址
1.2 一维数组的初始化
数组的初始化,主要注意的一点是:
像平时初始化数组的基本操作都会,比如:1.先定义数组,再进行初始化 。2.在定义的同时对其初始化等等。在这里主要注意的是:定义数组时省略[ ]内元素总数
比如:
int array[];/*错误的数组定义*/
说明:定义数组时没有定义数组元素的个数,使用时就会发生异常,原因是内存中我们并没有为数组 array 开辟任何存储空间,数据自然无处存放。
int array[]={1,2,3,4,5};/*定义一个整型数组,同时初始化数组的 5 个元素*/
解释:定义数组时没有定义数组元素的个数,这个时候数组会自动确定大小,确定后数组的大小为 5个元素,当我们给的元素个数超过五个时,就会出现数组溢出错误。
正确的定义方式:
int array[10]={1,2,3,4,5,6,7,8,9,10};
定义了一个整型数组,表示array数组有10元素,并且对数组中的10个元素进行初始化。
int array[10]={1,2,3,4,5};
定义了一个整型数组,表示array数组有10元素,仅仅对前面五个进行初始化,后面 5 个元素编译器自动初始化为 0 。
int array[100]={0};
定义了一个整型数组,数组大小为100的整型元素,对100个元素初始化为 0 。当我们不知道初始化什么数据时,可以进行这样的操作。
1.3 一维数组作为参数传递
将数组作为参数进行传递有两种传递方法,拿int类型的数组举例说明。一种是function (int arr [ ]);另一种是function (int *parr)
(1)function (int arr [ ]):
#include<stdio.h>
void function(int arr[])
{
int i = 0;
for (i = 0; i <5; i++) /*对数组array进行赋值*/
{
arr[i] = i;
}
}
int main()
{
int i = 0;
int array[5] = { 0 };
function(array);
for (i = 0; i < 5; i++) /*打印出数组array数组的元素*/
{
printf("%d ",array[i]);
}
return 0;
}
(2)function (int *parr) :
#include<stdio.h>
void function (int* parr)
{
int i = 0;
for (i = 0; i < 5; i++)
{
*(parr+i) = i ;/*首地址 + i,向前走 i*4个字节后,解引用操作对array数组赋值*/
}
}
int main()
{
int arr[5] = { 0 };
function (arr);
for (int i = 0; i < 5; i++)/*打印出数组array数组的元素*/
{
printf("%d ", arr[i]);
}
return 0;
}
说明:从这两个例子可以看出,使用这两种方法将数组作为参数进行传递,结果都是一样的。
注意:调用函数时,变量是按照传值方式传递的,而数组则是按照传地址方式传递的这就是说,对于变量,实参可以改变形参,形参的改变不会影响到实参;对于数组,实参可以改变形参,形参的改变也会改变实参。
2、二维数组
2.1 二维数组的定义
二维数组定义的一般形式为:
类型说明符 数组名[常量表达式][常量表达式];
例如:
int array[3][4];/*定义array为3行4列的数组*/
int code[5][10];/*code为5x10(5行10列)的数组*/
数组在内存中占用一块连续的存储区域,二维数组是什么样的情况?以array[3][4]为例,数组元素在内存中存储的形式如下所示。
二维数据是按照 “ Z ” 型存储的,把它展开,等效于下图的线状形式,从左至右地址逐渐递增,每个单元格占4个字节( 数组array是int类型 )。
数组存储地址的计算:二维数组为例,其他的依次类推: 假设起始下标从0开始,按行存储 (总共有M行,N列): A [i] [j]=A + (i*N+j)*L。
例如:已知 array[0][0]在内存中的地址,array[1][2]的地址是多少呢??计算方法如下:
array[1][2]的地址 =array[0][0]地址 +24 字节
24字节=(1行x4列+2列)x4字节
还需要注意到数组 array[3][4]元素下标的变化范围,行号范围是从 0 到 2,列号范围是以 0 到 3。
2.2 二维数组的初始化
1.先定义再初始化。例如:
int array[3][4];
array[0][0]=1;
array[0][1]=2;
2.定义的同时初始化。例如:
int array[3][4]={{1,2,3,4},{5,6,7,8},{9,10,11,12}};
或者:
int array[3][4]={1,2,3,4,5,6,7,8,9,10,11,12};
二维数组在内存中是按照线性顺序存储的,所以内存括号可以省去,不会产生影响。还可以如下:
int array[][4]={{1,2,3,4},{5,6,7,8},{9,10,11,12}};
或者:
int array[][4]={1,2,3,4,5,6,7,8,9,10,11,12};
省去 3 也是可以的,但是 4 是不能省去的。编译器会根据所赋的数值的个数及数组的列数自动计算出数组的行数。(行可以省略,列不可以省略)。
分析如下二维数组初始化后的值:
int array[3][4]={{1},{2},{3}};
拿上面的例子说明,我们可以认为二维数组是由 3 个一维数组构成的,每个一维数组有 4 个元素,那就可以和一维数组初始化衔接上了。经过上述初始化,数组array元素的值如表所示的形式。
array[0][0]=1 | array[0][1]=0 | array[0][2]=0 | array[0][3]=0 |
array[1][0]=2 | array[1][1]=0 | array[1][2]=0 | array[1][3]=0 |
array[2][0]=3 | array[2][1]=0 | array[2][2]=0 | array[2][3]=0 |
3、多维数组
C 语言中允许定义任意维数的数组,比较常见的多维数组是三维数组。可以形象地理解三维数组中的每一个对象就是三维空间中的一个点,它的坐标分别由 x、y和z等3个数据构成,其中x、y、z分别表示一个维度。
定义一个三维数组:
int array[2][3][4];
三维数组 array 由2*3*4=24 个元素组成,其中多维数组靠左边维变化的速度最慢,靠右边维变化的速度最快,从左至右逐渐增加。array 数组在内存中仍然是按照线性结构占据连续的存储单元,地址从低到高的顺序如下所示:
遍历三维数组,通常使用三重循环实现,这里就以array[2][3][4]数组为例说明。
#include<stdio.h>
int main()
{
int i = 0, j = 0, k = 0; /*定义循环变量*/
int array[2][3][4] = { 0 }; /*定义一个整型三维数组并赋值位零*/
for (i = 0; i < 2; i++) /*循环遍历数组,对其赋值*/
{
for (j = 0; j < 3; j++)
{
for (k = 0; k < 4; k++)
{
scanf("%d", &array[i][j][k]);/*对三维数组进行赋值*/
}
}
}
printf("打印出三维数组所有的元素:\n");
for (i = 0; i < 2; i++)/*循环遍历数组,打印出三维数组的所有元素*/
{
for (j = 0; j < 3; j++)
{
for (k = 0; k < 4; k++)
{
printf("%d ", array[i][j][k]);
}
printf("\n");
}
}
return 0;
}