第六章:数组
概述
从本章开始,就进入了C语言的进阶篇.前面讲的都是C语言最基本的知识.
本章将讲解数组.数组就是在一个变量名下存放多个数据的存储区的说明,是具有相同类型的数据的集合按照一定的顺序组成的数据.在处理大量数据的时候,使用数组十分的方便.
数组和其他类型的变量一样,必须要先定义后使用.
前面讲过,C语言中的数据类型可以分为基本类型,构造类型,指针类型和空类型,本章介绍的数组属于构造类型.
本章及后面的章节将主要介绍:一位数组,二维数组和字符数组(由于字符数组比较特殊,放在后面的章节专门讲)
不过C语言其实是支持高维数组的.
本章主要内容有:
- 数组的概述
- 一维数组
- 二维及高维数组
数组的概述
在许多数学问题中,进场遇到数列和矩阵的概念.
数列和矩阵是用于描述一批数据之间的关系的.
例如:数列X: X1,X2,X3,X4
矩阵Y: Y11,Y12,Y13,Y14
Y21,Y22,Y23,Y24矩阵和数列都有相同的特点:
- 有一批数据
- 数据之间存在内在的关系
- 所有的数据类型相同
因此C语言使用数组来表达数列和矩阵,数组和数列,矩阵的关系如下图:
正如前面所属的,数组也具有和矩阵及数列相同的特性:
- 有一批数据
- 这一批数据之中存在一定的内在联系
- 这些数据的数据类型相同
为了方便处理这些相同类型的数据,C语言使用数组来储存这些变量.
C语言在编译的时候会根据声明的数组大小来在内存中开辟一系列连续的空间作为数组的存储地址,
因此在声明数组的时候需要指定数组的大小
并且,声明数组的大小不可以用变量,因为内存分配在程序运行之前,故此时变量都只刚刚被划分内存空间而没有具体数值.
因此数组声明的一般形式是:
类型说明符 数组名 [常量表达式]……;
不同维度的数组声明下面将详细介绍
一维数组
本节将讲述一维数组的定义,引用和初始化.
由于一维数组是C语言中处理大量数据的首选,因此必须要掌握.
一维数组的定义
一维数组的定义方式:
类型说明 数组名[常量表达式];
其中:
- 类型说明符可以是int,float,char等
它表示每个数组元素的数据类型 - 数组名的命名和变量的命名规则一致
- 常量表达式的值是数组的长度,即数组中所包含的元素个数
例如:
int age[10];
关于一维数组需要说明的点如下:
-
同一个数组,所有元素的数据类型都是相同的
-
数组中元素的计数是从0开始的.例如:
int a[5]={32,12,24,56,23]; >>>a[0]=32 a[1]=12 a[2]=24 a[3]=56 a[4]=23
-
在定义的时候不能用变量来定义数组长度,例如:
int i; i=9; int a[i]; >>>编译不通过,会报错
-
允许在同一行语句中说明多个数组和变量.
int a[10],b[20];
-
声明数组的时候系统会划分一系列连续的内存空间,如下图
一维数组的初始化
一维数组的初始化,分成两种,分别和变量的初始化对应.
第一种是在数组声明时候就初始化
第二种是在数组声明之后才初始化
下面将分类介绍
数组声明时初始化
数组声明时初始化就是指在声明数组的时候就为数组内的元素赋值.语法如下
类型符 数组名[元素个数]={常量表};
例如:
int a[5]={1,2,3,4,5};
需要说明的点如下:
-
若给元素的所有值赋值,那么常量表达式可以省略
int a[]={1,2,3,4,5};
此时a的长度就为5
-
也可以给一部分元素赋值,这个时候长达表达式不能省略,而且未被赋值的元素默认为0
int a[5]={1,2,3}; >>>a={1,2,3,0,0}
-
如果要是全部元素都为0,可以将其定义为全局变量或者静态变量.
也可以是:int a[5]={0}; int a[5]={0,0,0,0,0};
-
若数组在定义时未初始化,则元素的值是随机的,原因和变量只声明未定义的原因一样
-
常量表的值多了的话,不会报错,多余的值不会被使用.但是不推荐这样
int a[5]={1,2,3,4,5,6}; >>>a[5]={1,2,3,4,5]
错误的声明例子如下:
float a[2],a=2.0; /*不能重名*/
char s[]; /*不赋值的时候不能省略长度*/
int n=3,m[n]; /*数组的长度必须是常量或者常量表达式,不能是变量*/
数组声明后初始化
数组声明后的初始化其实与数组元素的引用密切相关
因此将结合后面一维数组的引用介绍,这里只是说明,对于数组的初始化具有以上两种方法
一维数组的引用
数组必须要先定义,后引用.在定义了数组之后,数组的引用格式如下:
数组名[下标表达式]
数组元素实际上就是一种变量,他和一般的变量在运用上没有不同.可以打印,赋值,等等.
但是C预言不允许一次引用整个数组,因此我们常用循环的形式来遍历数组.
所以数组声明后的初始化其实就是引用元素后赋值,例如:
int a[5];
a[2]=2;
>>>a[2]=0;
但是关于一维数组的应引用还有说明的点.
主要就是关于内存中的地址的问题,这点在后面的指针中很有帮助.
我们实际上声明一个变量的时候只是在内存中划分了区域,而赋值的时候则是将缓存中的值存到划分的区域中.
因此由于数组是一个结构型数据,因此无法像基础变量类型一样可以直接得到整个数组的地址.
但是由于数组的本质是多个相同类型数据的集合,因此我们实际上可以通过取数组中元素的内存地址进而获得数组的地址.
取数组元素地址的操作如下:
数组元素的地址:数组名[i]或数组名+i
数组首地址(实际上是数组第一个元素的地址):数组名或&数组名[0]
例如:
int x[5];
x //取元素的首地址
&x[0] //取元素的首地址
x+i //取数组的x[i]元素的地址
&x[i] //取数组的x[i]元素的地址
二维及高维数组
二维数组相比于一维数组多了一个维度的扩展,因此在实际上具有更广泛的运用.
例如:灰度图像的储存,声音信号的储存(时间和强度两个维度),以及数学上的矩阵等等.
下面就将介绍二维以及高维数组的定义,初始化和引用(主要介绍二维,高维则加以推广)
二维数组的定义
二维数组定义的一般形式如下:
类型符 数组名[行数][列数]
在内存中的储存形式如下:
同样,高维数组的声明如下:
类型符 数组名[维度1长度][维度2长度]....[维度n长度]
在内存中的存储按照维度从前到后的顺序进行,遍历存储是从后到前
例如二维数组是先存储a[0][x],在a[1][x],最后是a[2][x].遍历则是x从0到3
二维数组的初始化
和一维数组的初始化一样,二维数组的初始化也分为声明时初始化和声明后初始化.
数组声明时初始化
有以下两种方式:
类型符 数组名[行数][列数]={常量表};
类型符 数组名[行数][列数]={{第一行常量表},{第二行常量表}}
其中第二中的可读性更高.
需要说明的点如下:
-
数组的存储是按照内存地址划分的顺序进行的,对于二维数组而言就是按行存储.
-
如果数据不够的时候,剩下的元素值都为0
-
如果给所有元素赋初值,那么行数可以省略,但是列数不能省略.
例如:int a[][3]={1,2,3,4,5,6};
-
和一维数组一样,如果二维数组只进行了定义而没有初始化,那么各元素的值是随机的.
数组声明后初始化
其实就是对二维数组引用之后赋值,略
二维数组的引用
二维数组的也需要先定义后引用.引用语法如下:
数组名[行下标][列下标]
关于二维数组的内存地址相关说明如下:
元素首地址: 数组名或数组名[0]或数组名[0][0]
元素i行首地址: 数组名[i]或数组名+i
元素地址: &数组名[i][j]或&数组名[i]+j
例如:
a[m][n] /*数组a的m行n列对应元素的地址*/
a,a[0],&a[0][0] /*数组a首地址*/
a[i],a+i /*二维数组第i行数组元素的首地址*/
&a[i][j],x[i]+j /*二维数组元素a[i][j]的地址*/
用
二维数组的也需要先定义后引用.引用语法如下:
数组名[行下标][列下标]
关于二维数组的内存地址相关说明如下:
元素首地址: 数组名或数组名[0]或数组名[0][0]
元素i行首地址: 数组名[i]或数组名+i
元素地址: &数组名[i][j]或&数组名[i]+j
例如:
a[m][n] /*数组a的m行n列对应元素的地址*/
a,a[0],&a[0][0] /*数组a首地址*/
a[i],a+i /*二维数组第i行数组元素的首地址*/
&a[i][j],x[i]+j /*二维数组元素a[i][j]的地址*/