3.3 数组
数组是一组具有固定数目的相同类型成分分量的有序数据集合。数组是C语言提供的一种最简单的构造类型,其成分分量的类型为该数组的基本类型。整型变量的有序集合称为整型数组,字符型变量的有序集合称为字符型数组。数组中的每个元素都属于同一个数据类型,在同一数组中不允许出现不同类型的变量。
在数组中,可以用一个统一的数组名和下标来唯一地确定数组中的元素。数组中的下标放在方括号中,是从0开始(0,1,2,3,4,…,n)的一组有序整数。例如,数组a[i],当i=0,1,2,3,…,n时,a[0],a[1],a[2],…,a[n]分别是数组a[i]的元素。数组中有一维、二维、三维和多维数组之分,常用的有一维、二维和字符数组。
1.一维数组
1)一维数组的定义数组只有一个下标时,称为一维数组。在C语言中,使用数组之前,需先对其进行定义。一维数组的定义方式如下:
类型说明符 数组名[常量表达式];
其中,类型说明符是任一种基本数据类型或构造数据类型(如int,char等)。数组名是用户定义的数组标识符,即合法的标识符。方括号中的常量表达式表示数组元素的个数,也称为数组的长度。例如:
unsigned int a[8]; //定义了含有8个元素的无符号整型数组a float b[10],c[16]; //定义了含有10个元素的实型数组b,含有20个元素的实型数组c unsigned char ch[20]; //定义了含有20个元素的字符数组ch
对于数组类型的定义应注意以下几点。
(1)数组名的定义规则和变量名相同,应遵循标识符命名规则。在同一程序中,数组名不能重名,即不能与其他变量名相同。
(2)数组名后是用方括号括起来的常量表达式,不能使用圆括号。
(3)方括号中的常量表达式表示数组元素的个数,如a[10]表示数组a有10个元素。每个元素由不同的下标表示,在数组中的下标是从0开始计算,而不是从1开始计算的。因此,a的10个元素分别为a[0],a[1],…,a[9]。注意,a[10]这个数组中并没有a[10]这个数组元素。
(4)常量表达式中可以包括常量和符号常量,不能包含变量。也就是说,C语言中数组元素的个数不能在程序运行过程中根据变量值的不同而随机修改,数组的元素个数在程序编译阶段就已经确定了。
2)一维数组元素的引用定义一维数组之后,就可以引用这个一维数组中的任何元素,且只能逐个引用而不能一次引用整个数组的元素。引用数组元素的一般形式如下:
数组名[下标]
这种引用数组元素的方法称为“下标法”。C语言规定,以下标法使用数组元素时,下标可以越界,即下标可以不在0~(长度-1)的范围内。例如,定义数组为a[3],能合法使用的数组元素是a[0]、a[1]、a[2],而a[3]、a[4]虽然也能使用,但由于下标越界,超出数组元素的范围,所以程序运行时,可能会出现不可预料的结果。
例如:对10个元素的数组进行赋值时,必须使用循环语句逐个输出各个变量,如下所示。
int i,a[10]; //定义变量i及含10个元素的一维数组a for(i=0;i<10;i++) { a[i]=0; }
而不能类似于下列的方法用一个语句输出整个数组变量。
int i,a[10]; a=0;
3)一维数组的初始化除了可以使用赋值语句对数组元素赋值外,还可以采用初始化赋值和动态赋值的方法给数组赋值。
数组初始化是指在定义数组的同时给数组元素赋值。虽然数组赋值可以在程序运行期间用赋值语句进行赋值,但是这样将耗费大量的运行时间,尤其是对大型数组而言,这种情况更加突出。采用数组初始化的方式赋值时,由于数组初始化是在编译阶段进行的,这样将减少运行时间,提高效率。
一维数组初始化赋值的一般形式如下:
类型说明符 数组名[常量表达式]={值,值,值,……,值};
其中,在“{}”中的各数据值即为各元素的初值,各值之间用逗号间隔。例如:
flash unsigned char tab[8]={0xfe,0xfd,0xfb,0xf7,0xef,0xdf,0xbf,0x7f};
经过上述定义的初始化后,各个变量值为:tab[0]=0xfe;tab[1]=0xfd;tab[2]=0xfb;tab[3]=0xf7;tab[4]=0xef;tab[5]=0xdf;tab[6]=0xbf;tab[7]=0x7f。
C语言对一维数组元素的初始化赋值还有以下特例。
(1)只给一部分元素赋初值:当“{}”中值的个数少于元素个数时,可以只给前面部分元素赋值。例如:
flash unsigned char tab[10]={0x00,0x00,0x07,0x02,0x02,0x02,0x7F};
在此语句中,定义tab数组有10个元素,但“{}”内只提供了7个初值,这表示只给前面7个元素赋值,后面3个元素的初值为0。
(2)给全部元素赋值相同值时,应在“{}”内将每个值都写上。例如:
int a[10]={2,2,2,2,2,2,2,2,2,2};
而不能写为
int a[10]=2;
(3)给全部元素赋值,在数组说明中进行时,可以不给出数组元素的个数。例如:
flash unsigned char tab1[24]={0x00,0x00,0x7F,0x1E,0x12,0x02,0x7F,0x00, 0x00,0x00,0x07,0x02,0x02,0x02,0x7F,0x00, 0x00,0x00,0x7F,0x1E,0x12,0x02,0x7F,0x00};
可以写为
flash unsigned char tab1[]={0x00,0x00,0x7F,0x1E,0x12,0x02,0x7F,0x00, 0x00,0x00,0x07,0x02,0x02,0x02,0x7F,0x00, 0x00,0x00,0x7F,0x1E,0x12,0x02,0x7F,0x00};
由于数组tab1初始化时“{}”内有24个数,所以系统自定义tab1的数组个数为24,并将这24个字符分配给24个数组元素。
2.二维数组
C语言允许使用多维数组,最简单的多维数组就是二维数组。实际上,二维数组是以一维数组为元素构成的数组。二维数组的定义方式如下:
类型说明符 数组名[常量表达式1][常量表达式2];
其中,常量表达式1表示第1维下标的长度,常量表达式2表示第2维下标的长度。二维数组存取顺序是:按行存取,先存取第1行元素的第0列,1列,2列,……,直到第1行的最后一列;然后返回到第2行开始,再存取第2行的第0列,1列,2列,……,直到第2行的最后一列。以此类推,直到最后一行的最后一列存完为止。例如:
int a[4][6]
该列定义了4行6列共24个元素的二维数组a[][],其存取顺序如下:
1)二维数组元素的引用二维数组元素引用的一般形式为
数组名[下标][下标]
其中,下标可以是整数,也可以是整数表达式。例如:
a[2][4] //表示a数组第2行第4列的元素 a[3-1][2*2-1] //不要写成a[2,3],也不要写成a[3-1,2*2-1]的形式
在使用数组时,下标值应在已定义的数组大小范围之内,以避免越界错误。例如:
int a[3][4]; ︙ a[3][4]=4; //定义a为3×4的数组,其行下标值最大为2,列坐标值最大为3,而a[3][4]超 //过数组范围
2)二维数组的初始化二维数组初始化也是指在类型说明时给各下标变量赋以初值。对二维数组赋值时可以按以下方法进行。
(1)按行分段赋值:按行分段赋值是指将第1个“{}”内的数值赋给第1行的元素,将第2个“{}”内的数值赋给第2行的元素,以此类推。采用这种方法比较直观,例如:
flash unsigned char tab[3][4]={{0x00,0x00,0x7F,0x1E},{0x12,0x02,0x7F,0x00}, {0x02,0x02,0x7F,0x00}};
(2)按行连续赋值:按行连续赋值是指将所有数据写在1个“{}”内,按数组排列的顺序对各个元素赋初值。例如:
flash unsigned char tab[3][4]={0x00,0x00,0x7F,0x1E,0x12,0x02,0x7F,0x00,0x02, 0x02,0x7F,0x00};
从这段赋值可以看出,第②种方法与第①种方法完成相同任务,都是定义同一个二维数组tab且赋相同的初始值,但是第②种方法没有第①种方法直观,如果二维数组需要赋的初始值比较多时,采用第②种方法将会在“{}”内写一大片,容易遗漏,也不容易检查。
(3)对部分元素赋初值:可以对二维数组的部分元素赋初值,未赋值的元素自动取“0”值。例如:
int a[3][4]={{1},{3},{6}}; //二维数组a各元素的值为{{1,0,0,0},{3,0, //0,0},{6,0,0,0}} int b[3][4]={{2},{1,3},{2,4,3}}; //二维数组b各元素的值为{{2,0,0,0},{1,3, //0,0},{2,4,3,0}} int c[3][4]={{2},{3,5}}; //二维数组c各元素的值为{{2,0,0,0},{3,5, //0,0},{0,0,0,0}} int d[3][4]={{1},{},{2,3,4}}; //二维数组d各元素的值为{{1,0,0,0},{0,0, //0,0},{2,3,4,0}}
(4)给元素赋初值时,可以不指定第1维的长度:如果对全部元素都赋初始值,则定义数组时对第1维的长度可以不指定,但第2维的长度不能省略。例如:
int a[3][4]={{1,2,3,4}{5,6,7,8}{9,10,11,12}};
与下面的定义等价:
int a[][4]={{1,2,3,4}{5,6,7,8}{9,10,11,12}};
如果只对部分元素赋初始值,则定义数组时对第1维的长度可以不指定,但第2维的长度不能省略,且应分行赋初始值。例如:
int a[][4]={{1,2,3},{},{5}};
该程序段定义了3行4列的二维数组,元素各初始值分别为{{1, 2, 3, 0},{0, 0, 0, 0},{5, 0, 0, 0}}。
3.字符数组
用来存放字符数据的数组称为字符数组。字符数组中的一个元素存放一个字符,因此可以用字符数组来存放长度不同的字符串。
1)字符数组的定义字符数组的定义与前面介绍的类似,即
(unsigned)char 数组名[常量表达式];
例如:
char a[10]; //定义了包含10个元素的字符数组a
字符数组也可以是二维或多维数组,和数值型多维数组相同。例如:
char b[3][5]; //定义了3行5列共15个元素的二维字符数组b
2)字符数组的引用字符数组的引用与数值型数组一样,只能按元素引用。
3)字符数组的初始化字符数组和数值型数组一样,也允许在定义时进行初始化赋值。例如:
unsigned char a[7]={‘p’,‘r’,‘o’,‘t’,‘e’,‘u’,‘s’} //将7个字符分别赋给a[0]~a[6]这7个元素
如果“{}”中提供的初值个数(即字符个数)大于数组长度,则C语言会将其作为语法错误处理。如果初值个数小于数组长度,则只将这些字符赋给数组中前面那些元素,其余的元素自动定义为空字符(即‘\0’)。对全体元素赋初值时,也可以省去长度。例如:
unsigned char a[13]={‘C’,‘o’,‘d’,‘e’,‘V’,‘i’,‘s’,‘i’,‘o’,‘n’,‘A’,‘V’,‘R’};
也可以写成:
unsigned char a[]={‘C’,‘o’,‘d’,‘e’,‘V’,‘i’,‘s’,‘i’,‘o’,‘n’,‘A’,‘V’,‘R’};
4)字符串和字符串结束标志字符串常量是由双引号括起来的一串字符。在C语言中,将字符串常量作为字符数组来处理。例如,在例3)中就是用一个一维字符型数组来存放一个字符串常量“CodeVisionAVR”的,这个字符串的实际长度与数组长度相等。如果字符串的实际长度与数组长度不相等时,为了测定字符串的实际长度,C语言规定以字符“\0”作为字符串结束标志,也就是说,在遇到第1个字符“\0”时,表示字符串结束,由它前面的字符组成字符串。
在C语言中没有专门的字符串变量,通常用一个字符数组来存放一个字符串。若将一个字符串存入一个数组时,也将结束符“\0”存入数组,并以此作为该字符串是否结束的标志。
如果将字符串直接给字符数组赋初值时,可采用类似于以下的两种方法:
unsigned char a[]={“CodeVisionAVR”}; unsigned char a[]=“CodeVisionAVR”;