这个问题关键在于理解指针为什么要有类型。其实不同类型的指针本质上都是一样的,即内存地址,地址本身是没有类型的,赋予指针类型的意义在于给出存储数据的字节宽度,也称数据对齐宽度。比如 int *pt,pt是一个整数指针,那么编译器便知道如果通过pt读取数据时需要一次读取4字节,因为那里存放着一个int。换句话说指针类型定义了数据访问时的字节长度。
int a[3][4]定义了一个固定长度的二维数组,其行宽为4列int,即4*4字节为一行,可见对齐宽度为16字节,也就是每16字节划分为一行;行数为3行。其中a代表了数组首地址,其类型为int (*)[4],地址类型中的长度参数([4])取自于数组定义,由编译器确定。数组定义中包含了维度信息和内存地址及其类型,当通过数组名访问元素时必须同时指定行列索引。
为了灵活高效地寻址,可以采用指针访问数组,那么指针类型必须和数组首地址匹配(一致),因此如此定义 int (*p)[4]。可以看出该定义与数组a的地址类型只是多了个p而已,也就是明确了指针名称。那么为什么编译器会将 int a[3][4]的地址定义为 int (*)[4]呢?
二维数组的指针,其运算的基本单位是行宽,因此二维数组的指针又称为行指针,假设a的首地址为1000,p指向a,当p++时地址值是增加了一行的宽度,也即加一后p的值为1016而不是1001也不是1004。因此int (*)[4]的含义为其数据宽度为4个int的指针,从而int (*p)[4]的含义为:p是一个指向行宽为4个int的指针,也即每个元素为4字节的int,而行总字节数为4*4=16。
解读此类定义时先从括号内开始,解读顺序为:括号内是个指针p,其括号右侧是数组长度,表明指针p指向的是一个含有4个元素的行,前缀(左侧)int定义了基本数据类型,即元素类型为int,那么行宽就是4个int组成,就是4*4字节。
说了一大堆,那么如果你看明白了,p=a就不用解释了吧(赋值a的地址)。
int (*p)[4]是一个定义数组指针;
p = a;是让这个指针指向a数组;
记住,要想用指针访问数组:
一维数组 -> 一级指针
二维数组 -> 数组指针
指针数组-> 二级指针
p[2]是合法的,因为a本身为一维数组,而p也是一维指针,所以这样没问题。p[2]相当于*(p+2),也就是a[2]。
但是q[2][3]是绝对错误的,因为b是一个二维数组,而q是一维指针,所以不能这样调用。另外,q=b也是错误的,理由同上,q=&b[0][0]就对了,你可以自己思考一下为什么。
定义一个指向数组的指针 并初始化指针的值