指针数组:首先它是一个数组,数组的元素都是指针。它是“储存指针的数组”的简称。int *p1[10];
数组指针:首先它是一个指针,它指向一个数组。它是“指向数组的指针”的简称。 int (*p2)[10];
这里需要明白一个符号之间的优先级问题,关键在于p1、p2先和谁结合。
“[]”的优先级比“*”要高。p1 先与“[]”结合,构成一个数组的定义,数组名为 p1,int *修饰的是数组的内容,即数组的每个元素。那现在我们清楚,这是一个数组,其包含 10 个指向 int 类型数据的指针,即指针数组。
至于 p2 就更好理解了,在这里“()”的优先级比“[]”高,“*”号和 p2 构成一个指针的定义,指针变量名为 p2,int 修饰的是数组的内容,即数组的每个元素。数组在这里并没有名字,是个匿名数组。那现在我们清楚 p2 是一个指针,它指向一个包含 10 个 int 类型数据的数组,即数组指针。
我们可以借助下面的图加深理解:
上述摘自:《C语言深度解剖》
关于指针数组和数组指针的代码:
#include <iostream> using namespace std; int main() { int *p1[3]; int (*p2)[3]; int a[3] = {1,2,3}; int b[2][3] = {{1,2,3}, {4,5,6}}; p1[0] = &a[0];//int * p1[1] = &a[1];//int * p1[2] = a + 2;//int * for (int i = 0; i < 3; i++) cout << *p1[i] << " "; //output:1 2 3 p2 = &a; //int (*)[3] for (int i = 0; i < 3; i++) cout << (*p2)[i] << " "; //output:1 2 3 p2 = &b[1]; //int (*)[3] for (int i = 0; i < 3; i++) cout << (*p2)[i] << " "; //output:4 5 6 }
明确两点
1. int a[3] = {1, 2, 3};
a是数组的首元素的首地址,具体地说是一个具有3个整型变量的数组的名字,而&a是整个数组的首地址,其值相同但意义不同。
编译器根据我们提供的类型和数组大小,为我们分配了适当大小的存储区域,并且把这块存储区域叫做a。请注意 :&a,它的类型是“指向具有 3 个整型变量数组的指针“,简而言之&a是一个数组指针。
2. &a+1
式子&a+1表示的是指针加法运算,而不是普通的数值加法运算,之所以会这样是因为&a是一个指针而非普通数值(虽然它本质上也是一个整数)。假如此时&a=0xFFFF5700,那么&a+1是多少呢?答案是:取决于&a的类型 。
a) 如果&a是一个指向char型的指针,那么&a+1 = 0xFFFF5701
b) 如果&a是一个指向short型的指针,那么&a+1 = 0xFFFF5702
c) 如果&a是一个指向int型的指针,那么&a+1 = 0xFFFF5704 (32位机器)
d) 如果&a是一个指向某种结构体struct foo的指针,那么&a+1 = 0xFFFF5700+sizeof(struct foo)
……
指针加1不是指针内容简单地加1,而是让指针指向下一个数据,这个数据的类型就是指针指向的类型,所以指针的加法究竟会让这个指针指向哪里,取决于这个指针指向的数据类型。
因此,综上所述,当&a与整数1做加法时,实际上是指针的加法,加1的含义是:令指针a指向下一个数据 ,下一个数据是啥?当然是紧挨着的下一个具有3个整型变量的数组了(因为&a的类型是指向具有3个整型变量的数组的指针)。
扩展阅读:林世霖 -- 一道试题引发的血案