仅用一个下标编号的数组元素的数组是一维数组,有一个以上的下标编号的数组元素的数组称为多维数组。数组用四字概括:同类、有序。
一、一维数组的定义和引用
1、一维数组的定义
一维数组的定义形式表示如下:
[存储类型符] 数据类型符 数组变量名[整型常量表达式];
说明:(1) 存储类型符表示数组中各元素的存储类别。
(2)数据类型符表示数组元素的数据类型,可以是任何数据类型。例如:int型、float型、char型等。
(3)数组变量名命名规则与变量名相同,要符合C语言标识符的命名规则。表示组数组在内存中的起始地址,也是数组第一个数组元素在内存中的地址。
(4)“[ ]”是数组的标志,其中间必须是整型常量或整型常量表达式,决定了数组中数组元素的个数,也称为数组的长度,必须是一个固定的值。
(5)最后以分号结尾。
下面为不同类型数据类型的数组定义:
#define N 5
int a[10]; //定义了有10个数据元素的int型数组a
float f[20]; //定义了有20个数据元素的float型数组f
char strl[10],str2[20]; //定义了有10个和20个数据元素的char型数组str1,str2
short b[2*5+N]; //定义了有15个数据元素的short型数组b
关于一维数组定义的说明如下:
(1)数组定义时,必须指定数组的大小(或长度),数组大小必须是整型常量表达式,不能是变量或变量表达式。以下为错误例子:
int n=10;
int a[n]; //数组的大小不能是变量
int b[10.3]; //数组的大小不能是浮点常量
int c[n+10]; //数组的大小不能是变量表达式
(2)数组定义后,系统将给其分配一定大小的内存单元,其内存单元的大小与数组元素的类型和数组的长度有关。计算数组所占内存单元的字节数的公式如下:
数组所占内存单元的字节数=数组大小 * sizeof (数组元素类型)
例如:short int a[20];则数组a所占内存单元的大小为20*sizeof(short)=20*2=40(字节)
(3)数组中每个数组元素的类型均相同,它们占用内存中连续的存储单元,其中第一个数组元素的地址是整个数组所占内存块的低地址,也是数组所占内存块的首地址,最后一个数组的元素的地址是整个数组所占内存块的高地址(末地址)。
2、一维数组的引用
数组是一种数据单元的序列,不能直接存取整个数组,只能引用数组中的各个数据单元。
引用单元的格式为:
数组变量名[ 下标]
下标可以是整型常量、整型变量或整型表达式。下标的最小值是0,最大值则是数组大小减1。
例如:short int a[10];则系统将该数组a分配10个short型单元(每单元2字节)的内存块,其中数组第一个元素是a[0] ,第2个元素a[1],...,第10个元素a[9]。
数组定义后,数组中的每一个元素其实就相当于一个变量,所以有时也把数组元素称为下标变量。对变量的一切操作也适合于数组元素。例如:
a[0]=2; //将数组a的第1个元素赋值为2
a[1]=4; //将数组a的第2个元素赋值为4
a[2]=a[0]+a[1]; //将数组a的第1个元素的值与第2个元素的值相加赋给第3个元素
数组的定义与引用在形式上非常类似,但含义不完全相同,要注意二者差距以便区分。
数组的定义前面一定带有数据类型符,而数组的引用则不带数据类型符。
int a[10]; //定义具有10个数据元素的int型数组
int x=a[8]; //对数组a的第9个数据元素进行引用
下标运算符 [ ] 是优先级最高的运算符之一。C系统会根据以下公式,用下标值计算出要引用的“存储单元的有效地址”。
有效地址=数组的起始地址+下标*sizeof(数组元素类型)
计算出有效地址后,C语言不会帮助检查这个地址是否在定义的数组单元范围之内。如果这个地址越界,系统在运行与编译时也不提供任何错误提示,程序继续执行,并访问相应的存储单元,而这个存储单元可能属于其他变量或不存在。
例如:对于上面定义的数组a来说:
short x=a[10]; //引用越界,a[10]的地址为:2000+10*2=2020,只能引用a[1]~a[9]
对于已定义的数组,每个数组元素对应具体的内存单元,因此对数组元素可以使用取地址运算符“&”来得到该数组单元的内存地址。如,&a[0]是数组元素的第一个单元的地址,它与数组变量名a的值相等。
注意:数组变量名是数组变量在内存中的起始地址,一旦定义了数组变量,这个地址就固定了,不能改变。它相当于一个地址常量。
int x=a[1]; //错误,应先定义数组a,再引用
int a[10];
二、一维数组的赋值
对一维数组的赋值通常有两种:一是在数组定义时赋初值,另一种是是先定义数组然后在程序中再对数组元素逐一赋值。
1、一维数组的初始化赋值
在定义数组时,可以对数组变量赋初始值,具体格式如下:
数据类型符 数组变量名[常量表达式]={表达式1,表达式2,...,表达式n};
初值列表
说明:
(1)“=”后面的表达式列表一定要用{ }括起来,被括起来的表达式被称为初值列表,表达式之间用英文逗号隔开。
(2)表达式的个数不能超过数组变量的大小。
int a[4]={1,2,3,4,5}; //超出了数组大小
(3)表达式1是第一个数组元素的值,表达式2是第二个数组元素的值,以此类推。例如,int a[5]={0,1,2,3,4};经过定义和初始化后,a[0]=0,a[1]=1,a[2]=2,a[3]=3,a[4]=4。
(4)如果表达式的个数小于数组的大小,则未指定值的数组元素被赋值为0。例如,int a[10]={0,1,2,3,4};经过定义和初始化后,a的前5个单元分别赋值成0、1、2、3、4,其余单元默认为0。
0 1 2 3 4 0 0 0 0 0
a[0] | a[1] | a[2] | a[3] | a[4] | a[5] | a[6] | a[7] | a[8] | a[9] |
(5)当对全部数组元素赋初值时,可以省略数组变量的大小,此时数组变量的实际大小就是初值列表中的表达式的个数。例如,char str[ ]={'a','b','c','d','e'};则数组str的实际大小为5。
注意: 在定义数组时,如果没有为数组变量赋初值,那么就不能省略数组的大小。而且数组不初始化,其数组元素的值为随机值。如:
int a[]; //错,没有指定数组的大小
2、一维数组在程序中赋值
C语言除了在定义数组变量时用初值列表对数组整体赋值外,无法再对数组变量进行整体赋值。
如下为错误的:
int a[5]; //正确例子
a={1,2,3,4,5};
a[ ]={1,2,3,4,5};
a[5]={1,2,3,4,5};
错误分析:1、a是数组名,表示数组在内存中的首地址,是一个地址常量,不能被赋值;2、“=”
右边的{1,2,3,4,5};与a[ ]不是合法的表达式;3、a[5]不是数组。
上述为定义数组,下面是对数组赋值,其中常见的方法如下:
(1)使用赋值语句逐一赋值。
这种较为简单有效,适用于长度较小的数组元素赋值,或对长度较大的数组的部分元素赋值,而且可对每个数组赋不同的值。
int a[4];
a[0]=1; a[1]=2; a[2]=3; a[3]=4; //将数值a的4个元素分别赋值为1、2、3、4
char str[80];
str[0]='b';str[1]='y';str[2]='e';str[3]='\0'; //将数组str赋值为一字符串“bye”
(2)使用循环语句来逐一赋值。
这种方法在变成中普遍使用的一种方法,适用于对某数组元素进行有规律的赋值或接收用户通过键盘输入对数组元素的赋值。
例如,下面的程序将数组a的各元素赋值成奇数序列。
int a[10],i;
for(i=0;i<10;i++)
a[i]=2*i+1;
下面的程序接收用户键盘输入赋值给数组各元素。
int a[10],i;
for(i=0;i<10;i++)
scanf("%d",&a[i]);
但要注意不能用scanf函数对数组进行所谓的“整体输入”。下面是错误案例:
int a[3];
scanf("%d%d%d",a);
scanf函数的第一个参数中有3个%d,因此后面必须跟着3个其他参数,不能只有一个参数a。可改为:
scanf("%d%d%d",&a[0],&a[1],&a[2]);
(3)使用memset函数来赋值。
标准库函数 memset可实现对某内存块的各字节单元整体赋同样的值。我们知道数组
在内存中是占用一片连续的存储块,所以在对数组各字节单元赋某个特定值的情况下,可以
其使用 memset来赋值而不必使用循环语句来进行。
memset函数原型如下:
void* memset(void*s, char ch, unsigned n)
其功能就是将s为首地址的一片连续的n个字节内存单元都赋值为ch。注意:是对内存的
每个字节单元都赋值为ch.所以 memset函数主要适合于字节型数组的整体赋值,当然对
非字节型数组进行清0也是可行的。形式参数s是一指针变量,代表某内存块的首地址。
例如,下面的程序是将数组str的每个数据单元赋值为'a':
char str[10];
memset(str, 'a',10):
再例如,下面的程序是将数组a的每个数据单元赋值为0(清0):
int a[10]:
memset(a, 0, 10*sizeof(int);
(4)使用memcpy函数实现数组间的赋值。
对于两个数据类型和大小相同的数组,如果将其中一个数组各单元的值要赋值给另
个数组的各数据单元,我们也许首先会想到用循环赋值的方式来解决,例如:
int a[5]={1,2,3,4,5},b[5],i;
for(i=0:i<5i++)
b[i]=a[i];
其实还有一个更简便的办法,就是使用 memepy库函数,该函数的原型如下:
void*memcpy(void*d, void*s, unsigned n)
其功能就是将s为首地址的一片连续的n个字节内存单元的值复制到以d为首地址的一片
连续的内存单元中。我们知道内存中最小的存储单位是字节,所以只要涉及内存数据块的
复制均可用 memepy函数来实现。由此可见, memepy函数其应用范围极广,在后面的程序
实例中,读者还可以看到它的应用。
对于前面的两个数组元素的赋值,如果用 memepy函数来实现,其语句为:
memcpy(b,a,5*size(int));
其结果与前面通过循环语句来实现数组间的赋值是完全相同的。