1.指针数组和数组指针的概念
指针数组:指针数组是一个数组,是指由指针构成的数组,在这个数组中的所有元素都是指针,每个指针占用相同的字节。
数组指针:数组指针是一个指针,是指一个数组的指针,数组两字表示这个指针指向一个数组的首地址,并且这个指针加1时移动的位置是整个数组的长度。
2.指针数组
一个可行的指针数组定义如下:
char *arr[4] = {"hello", "world", "xiang", "qian"};
这里的写法是*arr[4],则表示指针数组,如果是(*arr)[4],则表示数组指针。上面的代码中arr是一个有4个元素的数组,每个元素都是指针,因此arr占据的空间是14个字节,而后面的字符串呢?他们被分配在另一个内存区域,由arr指向他们,因此可以试着向字符串中多写入几个字母,再用sizeof(arr)输出观察,结果依然是16个字节。所以指针数组,相当于是把好几个指针放在了一个数组里,仅此而已。
3.指向数组的指针
对于一维数组,实际上数组的名字就是数组的首地址,数组的名字也可以当做指针使用。假设一个数组arr[5]存放了5个整型数字,在定义一个指针int *p = arr;那么arr[2]和p[2]的值是一样的。
而如果定义了一个数组指针指向了一个一维数组呢?如int (*p1)[5] = arr这时候相当于是一个指针,注意是一个!一个指针指向了一块内存区域,这块区域是个一维数组的首地址,而对这个指针进行加一操作的时候,这个指针实际上移动了整个数组长度的距离。这个可以类比来理解,指针之所以需要声明类型,是为了说明清楚指针它指向什么类型的数据,这样加一操作是才能知道移动多少距离,指向char的指针加一时移动1个字节,指向double的指针加一时移动8个字节。同样的道理,指向数组的数组指针如果写成int[5] (*p1)这样会比较好理解,这就告诉我们p1这个指针加一时一次要移动5个int的距离。实际中不能这么写,这只是为了好理解。
对于一维数组的访问,使用普通指针就够了,如果使用数组指针反而会比较麻烦。
#include <iostream>
using namespace std;
int main() {
int arr1[5] = {1, 2, 3, 4, 5};
int *p1;
int (*p2)[5] = &arr1;
//p1 = &arr1[0];
p1 = arr1;
cout << sizeof(arr1) << endl;
cout << &arr1 << '\t' << p1 << endl;
cout << &arr1 + 1 << '\t' << p1 + 1 << endl;
cout << endl;
cout << &arr1 << '\t' << p2 << endl;
cout << &arr1 + 1 << '\t' << p2 + 1 << endl;
cout << p1[2] << '\t' << *(*(p2+0)+2)<< endl; }
代码输出为:
20
0x28fef4 0x28fef4
0x28ff08 0x28fef8
0x28fef4 0x28fef4
0x28ff08 0x28ff08
3 3
对于二维数组的访问,也可以使用普通指针或者数组指针。假设一个数组arr2[3][3],定义一个普通指针获取arr2[0][0]的地址后,就可以操作其所有的元素,本质上这个数组在内存中的位置是连续的,因此我们当然知道对指针增加几个单位后会指向哪个位置。而使用数组指针时,int (*p2)[3] = arr2;一定要记住p2后面的方括号里的数字要和数组的列数相同,因为这样对这个指针进行加一操作时,移动的单位就是数组的列数,相当于移动了一整行的距离。
#include <iostream>
using namespace std;
int main() {
int arr2[3][3] = {{1, 2, 3},
{4, 5, 6},
{7, 8, 9}};
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++)
cout << arr2[i][j] << ' ';
cout << endl;
}
int *p3 = &arr2[0][0];
cout << p3[0] << ' ' << p3[7] << endl;
int (*p4)[3] = arr2;
cout << arr2 << '\t' << p4 << endl;
cout << arr2 + 1 << '\t' << p4 + 1 << endl;
cout << 0x28fee8 - 0x28fedc << endl;
cout << *(*(p4+0)+1) << '\t' << *(*(p4+2)+0) << endl;
return 0;
}
输出为:
1 2 3
4 5 6
7 8 9
1 8
0x28fedc 0x28fedc
0x28fee8 0x28fee8
12
2 7