二维数组

数组里面存储的元素是数组的话得到的就是二维数组,采用如下方式定义:

int a[2][3] = { {1, 2, 3}, {4, 5, 6} };

该数组里面有两个元素,每个元素是一个含有三个int型成员的数组。以下是二维数组的内存图: 

还有一种虚拟的二维数组内存图,它将二维数组理解成二维矩阵,通过行与列的方式,有助于对二维数组的记忆,如下:

数组或许该这么定义

如果我们换一种方式来思考一维数组这种数据类型的话,那么对于二维数组的理解将会非常简单。以整型一维数组举例,我们通过int a[3]定义了一个一维数组a,它有三个int类型的成员。而我们知道,数组也是一种数据类型,那么,它到底是什么类型呢,也就是说,我们的数组a到底是什么类型? 答案是,每一个一维数组都是一种单独的数据类型,以a为例,它的类型可以理解为int[3],表示的是含有3个整型元素的数组类型。同理,int b[5],b表示的类型是int[5],表示的是含有5个整型元素的数组类型。那么我们的数组或许可以这么定义:

int[3] a;    //通过int[3]这种数据类型定义一个变量a,它有三个int型元素
int[3] a[2]; //通过int[3]这种数据类型定义一个数组a,它的每个元素都是int[3]类型

通过这种理解方式可以发现,二维数组本质上还是一维数组,可以将二维数组甚至是多维数组拆解成一层一层的一维数组,按照一维数组的性质进行分析(所有关于一维数组地址运算法则都适用,比如地址偏移计算,取地址,解地址)。

关于数组名

在二维数组中,关于数组名与数组首地址的讨论将更加复杂一些,我们来看一下int a[2][3]这个数组的数组名有哪些情况:

注意:

&a, a, a[0]的关系就好比浙江省省政府,杭州市市政府,滨江区区政府,虽然这三个政府都在杭州,但是代表的身份完全不一样。

二维数组初始化

以下这些二维数组初始化都是合法的:

int a[3][3] = { {1,2,3}, {4,5,6}, {7,8,9} }; // 分行初始化 
int a[3][3] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 }; // 顺序初始化       
int a[][3] = {1, 2, 3, 4, 5, 6, 7 , 8 , 9 }; // 省略第一维的长度,建议不要采用这种做法
int a[3][3] = { {2}, {5, 6}, {7, 8, 9} };    // 按行部分元素初始化 

二级指针

指针变量也有地址,它的地址可以用一个二级指针来存放,如下:

int a = 3; 
int* p = &a;   //一级指针 
int** pp = &p; //二级指针

二级指针内存图:

通过二级指针访问变量:

*pp  ==> p 
**pp ==> a

数组指针

数组指针本质上还是一个指针,只不过它指向的是整个数组。按照我们刚才对数组的理解,那么,要定义一个指向整个数组的指针,或许可以这么定义:

int[3] a; 
int[3] *p = &a; //定义一个指针p,它指向a这个变量的地址

可以用这种方式去理解,但形式上还是要遵从C语言的语法规定,C语言中,定义数组指针的方式如下:

int a[3]; 
int (*p)[3]; //定义一个数组指针p,它希望存储一个含有三成员的整型数组地址 
p = &a;      //让p指向数组a

以下是数组指针的内存图: 

既然p指向的是含有三个元素的整型数组类型(int[3]类型),那么,就不可以把除此之外的类型赋值给它,哪怕同样是整型,类似下面的写法是错误(Warning)的:

int a[5]; 
int (*p)[3] = &a; //类型不匹配,报Warning错误,由编译器进行强制类型转换


由于p指向了整个数组,那么,对p的算术运算将以整个数组大小为单位进行偏移。 

通过数组指针获取指向的数组成员的过程:

p ==> &a 
p[0] ==> a 
p[0][0] ==> a[0]

数组指针与二维数组

可以通过数组指针保存二维数组的数组名,因为二维数组的数组名代表的就是一个数组的地址,如下:

int a[2][3]; 
int (*p)[3] = a; //数组指针p指向二维数组首地址 

那么,在往后要使用a的地方都可以用p来代替:

p[1] ==> a[1] 
p[1][0] ==> a[1][0]

数组指针与指针数组

注意落脚点,数组指针的本质是一个指针,在32位系统下永远占用4个字节,而指针数组本质是一个数组,代表的是一串连续的内存空间,只不过由于它是一个指针数组,所以它里面存储的是地址值。它们的定义如下,注意区分有括号和没括号:

int *a[3];   //指针数组,整体占用12字节,可以存储三个整型地址
int (*a)[3]; //数组指针,占用4个字节,希望存储一个int[3]类型的地址



  • 无标签