一、一维数组
1、定义
定义格式:类型 变量名称[元素数量] ;
例如:int grades[100] ;
、double weight[20] ;
说明:
- 元素数量也即是数组长度;
- 数组长度必须是整数,在编译时必须是确定的字面量(C99之前)。C99之后,可以是任何(整数)常量表达式,为了便于调整数组长度,可用宏定义数组长度。
- 数组定义后,再次调用整个数组,不需要写
[]
,直接用数组名即可代表整个数组。例如:sizeof(a);
(求数组a占用的字节数)
数组特点:
- 数组是一个容器,其内部所有元素具有相同的数据类型;
- 一旦创建,不能改变大小;
- 数组中的元素在内存中是紧密依次排列的。
以int a[10]
为例:
- 一个
int
型数组; - 共10个单元:a[0], a[1], a[2], … a[9],依次紧密排列;
- 可以出现在赋值
=
的左边(左值)或右边,例如:a[2] = a[1]+6;
。
2、数组下标
定义:
使用数组时,放在[]
内的数字称为下标或索引。数组下标从0开始。长度为n的数组元素的下标是0 ~ n-1。
- 数组下标可以是任何整数表达式,如
a[i + j*10] = 0;
、a[i++] = 0;
- 无论读写,编译器不会检查数组下标是否越界。越界数组访问可能导致程序崩溃(segmentation fault)。只能考程序员写代码时保证不越界。
举例:int a[10];
程序中出现对a[10]
的读写都是越界了(有效下标为0 ~ 9)。
- 长度为0的数组可以存在,但是无用。
举例:
//统计0~9数字个数
const int number = 10; //定义数组长度
int x,i;
int count[number] = {
0}; //数组定义和初始化
scanf("%d",&x);
while(x != -1){
count[x]++;
scanf("%d",&x);
}
//遍历数组输出
for(i=0;i<number;i++){
printf("%d:%d次\n",i,count[i]);
}
3、数组 初始化
(1) 格式
数组类型 数组名称[数组长度] = {整型常量表达式列表};
例如:int a[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10} ;
(2) 说明
- 用花括号
{}
括起来的整型常量表达式列表称为数组初始化器。 - 若初始化器比数组短,则剩余元素默认为0。例如:
int a[10] = {1, 2, 3, 4, 5, 6} ;
等价于int a[10] = {1, 2, 3, 4, 5, 6, 0, 0, 0, 0} ;
- 全0初始化:
int a[10] = {0} ;
- 注意:
int a[10] = {2} ;
等价于int a[10] = {2, 0, 0, 0, 0, 0, 0, 0, 0, 0} ;
。全0初始化为特殊情况,其他值并不适用。且仅适用于数组定义时的初始化,例如:
int a[10] = {
0}; //合法初始化
int b[10];
b[10] = {
0}; //非法赋值
- 初始化器为空是非法的。
- 初始化器必须不长于数组长度,否则也非法。
数组的集成初始化
- 若给定初始化器,可省略数组长度。编译器用初始化器的长度来确定数组大小。例如:
int a[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10} ;
- 花括号后面的分号
;
不能漏掉,这与块不同。
(3) 常见的初始化
① 初始化器比数组短
若初始化器比数组短,则剩余元素默认为0。例如:
int a[10] = {1, 2, 3, 4, 5, 6} ;
等价于int a[10] = {1, 2, 3, 4, 5, 6, 0, 0, 0, 0} ;
② 全0初始化
格式:int a[10] = {0} ;
注意:
int a[10] = {2} ;
等价于int a[10] = {2, 0, 0, 0, 0, 0, 0, 0, 0, 0} ;
。全0初始化为特殊情况,其他值并不适用。
③ 定位初始化(数组指示器)
- [i]和整型常量表达式一起组成一个指示器,用[i]在初始化数据中给出定位;
- 没有定位的数据接在前面相邻有定位元素后面;
- 其余位置补0;
也可以不给出数组长度,让编译器算;
适合初始化数据特别稀疏的数组;
例如:
int a[10] = { [0] = 2, [2] = 3, 6} ;
等价于int a[10] = { 2, 0, 3, 6, 0, 0, 0, 0, 0, 0} ;
int a[] = { [0] = 2, [2] = 3, 6} ;
等价于int a[4] = { 2, 0, 3, 6} ;
不要求赋值顺序:
int a[10] = {[2] = 3, [0] = 2 , 6} ;
等价于int a[10] = { 2, 0, 3, 6, 0, 0, 0, 0, 0, 0} ;
4、求数组长度sizeof
- 求数组长度:
sizeof(数组名)/sizeof(a[0]) ;
即:数组长度 = 整个数组所占字节数/单个元素所占字节数。例如:sizeof(a)/sizeof(a[0]) ;
sizeof
使用的格式说明选取%lu
(unsigned long
C89中最大的无符号整型)。C99中可使用更长的%zu。
例如:
#define SIZE sizeof(a)/sizeof(a[0])
int main(void){
int a[] = {
4,9,1,8,[0]=5,7};
int i;
printf("sizeof(a[]) = %lu\n",SIZE);
for(i=0; i<SIZE; i++) {
printf("%d ",a[i]);
}
return 0;
}
显示结果为:
注意:数组a长度为4,不是6。编译器默认用’4’, ‘9’, ‘1’, '8’初始化元素0~3,遇到指示器后用’5’初始化元素0,紧随其后的’7’初始化元素1,结束。
5、其他
- 一个数组不能直接对另一个数组整体赋值。例如
int a[5] = {
1, 2, 3, 4, 5};
int b[5] = a; //非法赋值
- 数组变量本身不能被赋值;
- 要把一个数组的所有元素交给另一个数组,必须采用遍历。
- 遍历数组时通常使用
for
循环。
对数组整体赋值(遍历):
for(i=0; i<length; i++){
b[i] = a[i];
}
-
遍历数组的常见错误:
- 从0开始,则应以<数组长度结束,而非<=;
- 离开循环之后,继续用i的值表示数组元素下标。此时i=数组长度,但越界(0~n-1)。
-
对数组整体赋值,除了逐个遍历之外,还可使用
<string.h>
头的memcpy
函数,特别是处理大型数组时,速度更快。例如:
#include <string.h> //调用此header中的memcpy函数
int main(void){
int a[] = {
0,1,2,3,4,5,6};
int b[7];
int i;
memcpy( b, a, sizeof(b) ); //把数组a中的元素复制到b中
printf("b[10] = {")