数组是一组具有固定数目的相同类型成分分量的有序数据集合。数组是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)一维数组 数组只有一个下标时,称为一维数组。在C语言中,使用数组前,需先对其进行定义。一维数组的定义方式如下:
其中,类型说明符是任一种基本数据类型或构造数据类型(如int,char等)。数组名是用户定义的数组标识符,即合法的标识符。方括号中的常量表达式表示数据元素的个数,也称为数组的长度。例如:
对于数组类型的定义应注意以下4点。
● 数组名的定义规则和变量名相同,应遵循标识符命名规则。在同一程序中,数组名不能重名,即不能与其他变量名相同。
● 数组名后是用方括号将常量表达式括起来的,不能用圆括号。
● 方括号中常量表达式表示数组元素的个数,如a[10]表示数组a有10个元素。每个元素由不同的下标表示,在数组中的下标是从0开始计算,而不是从1开始计算。因此,a的10个元素分别为a[0],a[1],…,a[9]。注意,a[10]这个数组中并没a[10]这个数组元素。
● 常量表达式中可以包括常量和符号常量,不能包含变量,即C语言中数组元素个数不能在程序运行过程中根据变量值的不同而随机修改,数组的元素个数在程序编译阶段就已经确定了。
如果定义了一维数组后,就可以引用这个一维数组中的任何元素,且只能逐个引用而不能一次引用整个数组的元素。引用数组元素的一般形式如下:
这种引用数组元素的方法称为“下标法”。C语言规定,以下标法使用数组元素时,下标可以越界,即下标可以不在0~(长度-1)的范围内。例如,定义数组为a[3],能合法使用的数组元素是a[0]、a[1]、a[2],而a[3]、a[4]虽然也能使用,但由于下标越界,超出数组元素的范围,程序运行时,可能会出现不可预料的结果。
例如,对10个元素的数组进行赋值时,必须使用循环语句逐个输出各个变量:
而不能类似于下列的方法用一个语句输出整个数组变量:
给数组赋值的方法除了用赋值语句对数组元素赋值外,还可以采用初始化赋值和动态赋值的方法。
数组初始化是指在定义数组的同时给数组元素赋值。虽然数组赋值可以在程序运行期间用赋值语句进行赋值,但是这样将耗费大量的运行时间,尤其是对大型数组而言,这种情况更加突出。采用数组初始化的方式赋值时,由于数组初始化是在编译阶段进行的,这样将减少运行时间,提高效率。
一维数组初始化赋值的一般形式如下:
其中,在“{}”中的各数据值即为各元素的初值,各值之间用逗号间隔。例如:
经过上述定义的初始化后,各个变量值为: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)只给一部分元素赋初值:如果“{}”中值的个数少于元素个数时,可以只给前面部分元素赋值。例如,
在此语句中,定义了tab数组有10个元素,但“{}”内只提供了7个初值,这表示只给前面7个元素赋值,后面3个元素的初值为0。
(2)给全部元素赋值相同值:给全部元素赋相同值时,应在“{}”内将每个值都写上。例如,
而不能写为:
(3)给全部元素赋值,但不给出数组元素的个数。如果给全部元素赋值,则在数组说明中进行,可以不给出数组元素的个数。例如,
可以写为:
由于数组tab1初始化时“{}”内有24个数,因此,系统自定义tab1的数组个数为24,并将这24个字符分配给24个数组元素。
2)二维数组 C语言允许使用多维数组,最简单的多维数组就是二维数组。实际上,二维数组是以一维数组为元素构成的数组。二维数组的定义方式如下:
其中,常量表达式1表示第1维下标的长度,常量表达式2表示第2维下标的长度。二维数组存取顺序是按行存取,先存取第1行元素的第0列,第1列,第2列,…,直到第1行的最后一列;然后返回到第2行开始,再取第2行的第0列,第1列,第2列,…,直到第2行的最后一列;依次类推,直到最后一行的最后一列。例如:
该列定义了4行6列共24个元素的二维数组a[][],其存取顺序如下:
二维数组元素引用的一般形式为:
其中,下标可以是整数,也可以是整数表达式。例如:
在使用数组时,下标值应在已定义的数组大小范围内,以避免越界错误。例如:
二维数组初始化也是在类型说明时给各下标变量赋以初值。对二维数组赋值时,可以按以下方法进行。
(1)按行分段赋值:按行分段赋值是将第1个“{}”内的数值赋给第1行的元素,第2个“{}”内的数值赋给第2行的元素,依次类推。采用这种方法比较直观,例如,
(2)按行连续赋值:按行连续赋值是将所有数据写在1个“{}”内,按数组排列的顺序对各个元素赋初值。例如,
从这段赋值可以看出,第2种方法与第1种方法完成相同任务,都是定义同一个二维数组tab且赋相同的初始值,但是第2种方法不如第1种直观,如果二维数组需要赋的初始值比较多时,采用第2种方法将会在“{}”内写一大片,容易遗漏,也不容易检查。
(3)对部分元素赋初值:可以对二维数组的部分元素赋初值,未赋值的元素自动取“0”值。例如,
(4)元素赋初值时,可以不指定第1维的长度:如果对全部元素都赋初始值,则定义数组时对第1维的长度可以不指定,但第2维的长度不能省略。例如,
与下面的定义等价:
如果只对部分元素赋初始值,则定义数组时对第1维的长度可以不指定,但第2维的长度不能省略,且应分行赋初始值。例如:
该程序段定义了3行4列的二维数组,元素各初始值分别为{{1,2,3,0},{0,0,0,0},{5,0,0,0}}。
3)字符数组 用于存放字符数据的数组称为字符数组。字符数组中一个元素存放一个字符,所以可以用字符数组来存放长度不同的字符串。
字符数组的定义格式为:
例如:
字符数组也可以是二维或多维数组,和数值型多维数组相同。例如:
字符数组允许在定义时作初始化赋值。例如:
如果“{}”中提供的初值个数(即字符个数)大于数组长度,C语言作为语法错误处理。如果初值个数小于数组长度,则只将这些字符赋给数组中前面那些元素,其余的元素自动定义为空字符(即‘\0’)。对全体元素赋初值时,也可以省去长度。例如:
也可以写成:
字符串常量是由双引号括起来的一串字符。在C语言中,将字符串常量作为字符数组来处理。例如,在上例中就是用一个一维字符型数组来存放一个字符串常量“STC89C51”,这个字符串的实际长度与数组长度相等。如果字符串的实际长度与数组长度不相等时,为了测定字符串的实际长度,C语言规定以字符“\0”作为字符串结束标志,也就是说,在遇到第1个字符“\0”时,表示字符串结束,由它前面的字符组成字符串。
在C语言中没有专门的字符串变量,通常用一个字符数组来存放一个字符串。若将一个字符串存入一个数组时,也将结束符“\0”存入数组,并以此作为该字符串是否结束的标志。
如果将字符串直接给字符数组赋初值时,可采用如下两种方法:
所谓指针,就是在内存中的地址,它可能是变量的地址,也可能是函数的入口地址。如果指针变量存储的地址是变量的地址,就称该指针为变量的指针(或变量指针);如果指针变量存储的地址是函数的入口地址,就称该指针为函数的指针(或函数指针)。
1)变量的指针和指向变量的指针变量 指针变量与变量指针的含义不同:指针变量也简称为指针,是指它是一个变量,且该变量是指针类型的;而变量指针是指它是一个变量的地址,该变量是指针类型的,且它存放另一个变量的地址。
指针定义的一般形式如下:
其中,类型标识符就是本指针变量所指向的变量的数据类型;“*”表示这是一个指针变量;指针变量名就是指针变量的名称。例如:
在定义指针变量时要注意以下两点。
● 指针变量名前的“*”表示该变量为指针变量,上例中的指针变量名为ap1、ap2、ap3、ap4,而不是*ap1、*ap2、*ap3、*ap4,这与定义变量有所不同。
● 一个指针变量只能指向同一个类型的变量,上例中的ap1只能指向整数型变量,不能时而指向字符型或实数型指针变量。
指针变量在使用前也要先定义说明,然后要赋予具体的值。指针变量的赋值只能赋予地址,而不能赋予任何其他数据,否则将引起错误。在C语言中,变量的地址由编译系统分配,用户不知道变量的具体地址。
有两个有关的运算符,即“&”和“*”,其中,“&”为取地址运算符;“*”为指针运算符(或称“间接访问”运算符)。在C语言中,指针变量的引用是通过取地址运算符“&”来实现的。使用取地址运算符“&”和赋值运算符“=”就可以使一个指针变量指向一个变量。
例如,指针变量p所对应的内存地址单元中装入了变量x所对应的内存单元地址,可使用以下程序段来实现:
还可以采用以下程序段来实现:
2)数组指针和指向数组的指针变量 既然指针可以指向变量,当然也可以指向数组。所谓数组的指针是指数组的起始地址,数组元素的指针是数组元素的地址。若有一个变量用于存放一个数组的起始地址(指针),则称之为指向数组的指针变量。
定义一个指向数组元素的指针变量的方法与指针变量的定义相同。例如:
在C语言中,数组名代表数组的首地址,也就是第0号元素的地址。因此,语句“P=&x[0];”和“P=x;”是等价的。还可以在定义指针变量时赋给初值,例如:
等价于:
如果p指向一个一维数组x[6],并且p已给它赋予了一个初值&x[0],可以使用以下3种方法引用数组元素。
● 下标法:C语言规定,如果指针变量p已指向数组中的一个元素,则p+1指向同一数组中的下一个元素。p+i和x+i,就是a[i],或者说它们都指向x数组的第i个元素。
● 地址法:*(p+i)和*(x+i)也就是x[i]。实际上,编译器对数组元素x[i]就是处理成*(x+i),即按数组的首地址加上相对位移量得到要找元素的地址,然后找出该单元中的内容。
● 指针法:用间接访问的方法来访问数组元素,指向数组的指针变量也可以带下标,如p[i]与*(p+i)等价。
3)字符串指针和指向字符串的指针变量 在C语言中有两种方法可以实现一个字符串运算:一种是使用字符数组来实现;另一种是用字符串指针来实现。例如:
字符串指针变量的定义说明与指向字符变量的指针变量说明是相同的。在上述程序段中,a[]是一个字符数组,字符数组是以“\0”常量结尾的;b是指向字符串的指针,它没有定义字符数组,由于C语言对字符串常量是按字符数组处理的,实际在使用字符串指针时,C编译器也在内存中开辟了一个字符数组用于存放字符串常量。
用字符数组和字符串指针变量都可实现字符串的存储和运算,但二者是有区别的。在使用时应注意以下4个问题。
● 字符串指针变量本身是一个变量,用于存放字符串的首地址。而字符串本身是存放在以该首地址为首的一块连续的内存空间中,并以‘\0’作为串的结束。字符数组是由于若干个数组元素组成的,它可用于存放整个字符串。
● 定义一个字符数组时,在编译中即已分配内存单元,有确定的地址。而定义一个字符指针变量时,给指针变量分配内存单元,但该指针变量具体指向哪个字符串并不知道,即指针变量存放的地址不确定。
● 赋值方式不同。对字符数组不能整体赋值,只能转化成分量,对单个元素进行赋值。而字符串指针变量赋值可整体进行,直接指向字符串首地址即可。
● 字符串指针变量的值在程序运行过程中可以改变,而字符数组名是一个常量,不能改变。