数组,作为C语言中常见的复杂数据类型,了解其本质有助于深入了解C语言
数组概念
元素类型角度:数组是相同类型的变量的有序集合测试指针变量占有内存空间大小
内存角度:联系的一大片内存空间
数组初始化
数组元素的个数可以显示或隐式指定
数组的初始化可以用{ 0 },也可以使用memset初始化
int main()
{
int i = 0;
int a[10] = { 1, 2 }; //其他初始化为0
int b[] = { 1, 2 };
int c[20] = { 0 };
for (i = 0; i < 10; i++)
{
printf("%d ", a[i]);
}
memset(a, 0, sizeof(a));
getchar();
return 0;
}
关于数组名的一些问题
- 数组首元素的地址和数组地址是两个不同的概念,前者代表一个元素大小,后者代表一个和数组大小
- 数组名代表数组首元素的地址,它是个常量,其本质就是一个变量的别名
- 数组首元素的地址和数组的地址值相等
- 数组的类型由元素类型和数组大小共同决定,e.g. int[5]
数组类型的自定义
1 | typedef int(MYINT5)[5]; |
数组指针
数组指针用于指向一个数组
- 通过数组类型定义数组指针
1
typedef int(ArrayType)[5]; ArrayType* pointer;
用数组类型加*定义一个数组指针
1 | void main(void) { int a[5];//声明一个数组类型 typedef int(MYINT5)[5];//用数组类型 加*,定义一个数组指针变量 MYINT5 *array; array = &a; for (i = 0; i < 5; i++) { (*array)[i] = i; } for (i = 0; i < 5; i++) { printf("\n%d %d", a[i], (*array)[i]); } } |
- 声明一个数组指针类型
1
typedef int (*MyPointer)[5];
定义一个数组指针类型,然后用类型定义变量
1 | void main(void) |
- 直接定义
1
int (*pointer)[n];
直接定义一个数组指针变量
1 | void main(void) |
一维数组本质
int a[5] 一维数组名代表数组首元素的地址
int a[5] ==> a的类型为int*
二维数组本质
实验:
1 | int arr[3][4]; |
此时输出打印的结果相差16,也就是4 * 4
,通过这个契机,得出二维数组名的本质
arr的本质是一个数组指针,每次向后移动一维的长度
说明 | 写法 | 写法 | 写法 |
---|---|---|---|
第0行第1列元素地址 | a [0]+1 |
*a+1 |
&a[0][1] |
第1行第2列元素地址 | a [1]+2 |
*(a+1)+2 |
&a[1][2] |
第 i 行第 j 列元素地址 | a [i]+j |
*(a+i)+j |
&a[i][j] |
第1行第2列元素的值 | *(a [1]+2) |
*( *(a+1)+2) |
a[1][2] |
第 i 行第 j 列元素的值 | *(a [i]+j) |
*( *(a+i)+j) |
a[i][j] |
正是因为有了这些特性,二维数组在做函数参数的时候,会退化为二级指针,这样设计的目的在于参数传递的时候不用传递太多数据,数组指针做函数参数,一般只会使用到二维char buf[3][4]
==> char buf[][4]
==> char (*buf)[4]
多维数组的线性存储特性
多维数组在内存中是线性存储的,即按照低维到高维线性排序
1 | char buf[2][3]; |
buf[0][0]·buf[0][1]·buf[0][2]·buf[1][0]·buf[1][1]·buf[1][2]
数组做函数参数
由于C语言中只会以机械式的值拷贝的方式传递参数(实参把值传给形参),所以在数组做函数参数的时候,是没法知道数组大小的,需要额外传递数组大小数据
1 | int fun(char a[20], size_t b) |
二维数组可以看做是一维数组
二维数组中的每个元素是一维数组
二维数组参数中第一维的参数可以省略void f(int a[5]); ==> void f(int a[]); ==> void f(int* a);
void g(int a[3][3]) ==> void g(int a[][3]); ==> void g(int (*a)[3]);