C/C++ 指针,数组与退化


  C/C++ 中数组与指针在使用上的关系及其复杂性是一个关键问题。

  首先说明运算符结合律优先级:int * > a[] > *a,后置递增运算符的优先级高于解引用运算符

  注意,某些教材中会提到行指针和列指针,我认为这是非常错误的叫法,只有指向数组的指针和指向数组元素的指针这两种称呼。 为什么这么说呢,因为你可以用一个指向数组元素的指针遍历整个数组,只要不越界就可以一直自增,不受列数限制,并且多维数组也更没有行列之分。

一维数组

  先声明一个一维数组:int a[2] = {1,2};

  这时候,我们可以声明一个指向该数组(首元素)的指针:int(*b) = a;

  此时 *(b++) == 2

  我们会发现,b 实际上仅仅作为 int 类型的指针,而数组名 a 退化为了数组首元素的指针。

  我们知道,a 的类型是一个 int 型数组,那么如何声明一个指向整个数组的指针呢:int(*c)[2] = &a;

  此时 (*c) 相当于 a 的一个别名,对 c 自增后,c 指向这个数组结束地址的下一个地址。

二维数组

  二维数组更复杂一点:int a2[2][3] = {1,2,3,4,5,6};

  根据前文,我们可以得到指向这个二维数组的指针的声明是这样的:int (*b2)[2][3] = &a2;

  同理,(*b2) 也可以认为是 a2 的一个别名,例如 (*b2)[0][0] == 1

  但是,我们知道,多维数组实际上是数组的数组,所以还可以使用 *(*b2)[0] 即先解引用 b,得到数组,再解引用数组,得到指向 a2[2] 的数组类型的指针,再用下标运算符去访问元素。

  那么 *(*(*b2)) 也是可行的,首先对 b2 进行解引用,b2 是一个指向二维数组的指针,解引用后得到二维数组,再对 (*b2) 进行解引用得到指向数组的指针,对二维数组解引用涉及到数组退化为指针,且只能逐级退化,防止歧义,所以 int *t = a2; 是非法的。

  网络上很多人把 int(*f)[3] = a2;f 说成是指向二维数组的指针,虽然他可以自增并遍历整个二维数组,但这明明是数组名退化后指向一维数组的指针,真是滑天下之大稽。

引用

引用的声明方法和指针是类似的,只不过引用的是对象本身而不是地址,例如 int (&e2)[2][3] = a2;

不过由于数组只会在初始化指针的时候退化成指针(根据指针的类型退化),所以不能幻想数组退化成指针然后初始化为引用。