数组
一维数组
一维数组初始化
- 在创建数组时,我们必须定义数组的类型和大小,数组的大小不能为0,数组中的元素类型都是相同的。
- 数组长度必须是固定的,必须为常量或常量表达式,不能使用变量进行初始化。
一维数组初始化
- 完整初始化:
int arr[3] = {1, 2, 3};
- 不完整初始化:
int arr[3] = {1, 2};
- 自动计算数组长度初始化:
int arr[] = {1, 2, 3};
注意:
- 静态初始化缺省情况将数组元素自动设置为0,自动初始化缺省情况下是未初始化的。
- 数组初始化元素的个数不允许超过数组长度:
int a[3] = {1,2,3,4}; // 不合法
字符数组的初始化
-
最笨拙的方式:
char message[] = { 'h','e','l','l','o',0 };
-
快速初始化方式:
char message[] = "hello";
这种初始化方式与上面的初始化方式是等效的,并且默认会在末尾'\0'
。 -
上面都是字符数组初始化的基本方式,如果要初始化字符串常量,则采用:
char *message = "hello";
。初始化字符数组和字符串常量的区别,可以表示如下:
作符返回一个指向数组的指针,而不是一个指向数组第一个元素的指针的指针。
注意:
-
数组名的值是一个指针常量,不能修改它的值。
int a[5]; int b[5]; a = b; // 操作违法,数组名是指针常量,不能改变它的值
一维数组的访问
- 一维数组是使用下标来访问的,下标从0开始。
- 数组的大小通过计算可以得到:
sz=sizeof(arr)/sizeof(arr[0]);
- 一维数组还可以通过指针来访问。
int array[10];
int *ap = array + 2;
表达式 | 含义 |
---|---|
ap | 与之对等的表达式是&array[2] |
*apy | 与之对等的表达式是array[2] |
ap[0] | c的下标引用和间接表达式是一致的,此处可以理解为*[ap+(0)] ,也就是*ap ,即array[2] |
ap+6 | 与之对等的表达式是&array[8] ,或者array+8 |
*ap+6 | 与之对等的表达式是array[2]+6 |
*(ap+6) | 与之对等的表达式是array[8] |
ap[6] | 与之对等的表达式是array[8] |
&ap | 表达式是合法的,但是找到与之对等的表达式,因为无法知道编译器将ap 放在相对于array 的什么位置 |
ap[-1] | 与之对等的表达式是array[1] |
ap[9] | 这个表达式是非法的,访问越界 |
考虑下面的表达式
2[array]
这个表达式是合法的,将其转换成对等的间接访问表达式为*(array+2)
,也就是array[2]
。但是为了程序的可读性,强烈不建议如此写法。
一维数组和指针
int a[5];
int *b;
声明一个数组时,编译器先根据指定的元素数量为数组保存内存空间,然后创建数组名,它的值是一个常量,指向这段空间的起始位置。
声明指针的时候,编译器只为指针本身保留内存空间,并且指针变量并没有被初始化指向任何的内存空间。
下两个声明都是正确的:
int strlen (char *string );
int strlen( char string[]);
如果函数需要知道数组的长度,那么它必须作为一个显示的形参传递给函数。
多维数组
当数组维数不止一个时,我们可以声明多维数组。
多维数组的存储顺序
int a[3][6]
内存中的存储顺序为:
看作一个初始化列表。
数组长度自动计算
int array[][5] = {
{0,1,2,3},
{4,}
{5,6}
}
对于上面的多维数组定义,编译器能够自动推断出数组的第一个维度是3。
多维数组的数组名
多维数组的数组名也是个指针常量,但是和一维数组不同,多维数组的数组名是指向数组第一行的常量指针,而不是指向第一个元素。
int matrix[3][10];
matrix
可以看作一个一维数组,包含3个元素,每个元素都是包含10个整型数的数组。
多维数组的下标
假设声明了int matrix[3][10];
表达式 | 含义 |
---|---|
matrix[1][5] |
表示数组的第二行第六个元素 |
matrix |
指向数组的第一行,它的类型:指向包含10个整型元素的数组的指针 |
matrix+1 |
指向数组的第二行,它的类型:指向包含10个整型元素的数组的指针 |
*(matrix+1) |
指向数组的第二行第一个元素的值,即&matrix[1][0] ,它的类型是:int* |
*(matrix+1)+5 |
指向数组的第二行第六个元素的值,即&matrix[1][5] ,它的类型是:int* |
*(*(matrix+1)+5) |
取右值,即a[1][5] |
指向多维数组的指针
int a1[10], *p1=a1;
int a2[3][10], *p2=a2;
第一个声明是正确的,第二个声明是不正确的,因为a2
的类型不是 int*
而是int (*p)[10]
。
所以上面的表达式应该更改为:
int a2[3][10];
int (*p2)[10] = a2;
它使p2指向a2的第一行。下面的两个声明都是使p2指向a2的第一个整型元素:
int *p2 = &a2[0][0]
int *p2 = &a2[0];
作为函数参数的多维数组
作为函数参数的多为数组名的传递方式和一维数组相同——实际传递的是数组的第一个元素。两者的区别是,多位数组的每个元素本身是一个数组,所以以下声明:
int arr[3][10];
...
fun(arr);
参数arr的类型是指向包含十个整形元素的数组的指针。fun的原型为如下两种形式中的任何一种:
void fun(int (*arr)[10]);
void fun(int arr[][10]);
指针数组
int *api[10];
因为下标引用的优先级高于间接访问,所以表达式先执行下标操作,所以api是某种类型的数组。对数组的某个元素执行间接访问操作后,得到一个整型,所以api的元素类型为指向整型的指针。
指针数组的应用,例如分析C语言中关键字的个数:
char const *keyword[] = {
"do",
"for",
"if",
"register",
"return",
"switch",
"while"
};
#define N_KEYWOLD \
(sizeof(keyword)/sizeof(keyword[0]))
存储结构:
如果使用矩阵存储关键字:
char const keyword[][9] = {
"do",
"for",
"if",
"register",
"return",
"switch",
"while"
};
此时的存储结构:
两种形式存储结构的比较:
- 如果存储的字符串长度差不多,那么采用矩阵形式更加紧凑。
- 如果字符串长度千差万别,并且大多数很短,极少数很长,此时使用指针数组的形式将更加紧凑。