数组指针和指针数组

1.数组指针

即数组的指针

1
2
3
4
5
6
int c[3];
int a[3] = {4, 5, 6}; //int型数组
int b[3] = {1, 6, 10};
int (*p)[3]; //数组指针,指向包含三个int型数组的指针
//优先级: ()>[]>*
p = &a;
1
2
3
4
5
6
cout << "数组a的起始地址:" << a << "\n"
<< "数组b的起始地址:" << b << endl;

-----------
数组a的起始地址:0x61fdfc
数组b的起始地址:0x61fdf0

可以看到数组a和数组b位于栈空间上,a的起始地址大于b

1
2
3
4
5
6
7
8
cout << "sizeof(a)=" << sizeof(a) << "\n"
<< "sizeof(p)=" << sizeof(p) << "\n"
<< "sizeof(*p)=" << sizeof(*p) << endl;

------------
sizeof(a)=12
sizeof(p)=8
sizeof(*p)=12
1
2
3
4
5
6
7
8
9
10
11
//对应头文件
#include<typeinfo>
#include<cxxabi.h>
cout << "p type:"<<abi::__cxa_demangle(typeid(p).name(), 0, 0, 0) << endl;
cout << "*p type:"<<abi::__cxa_demangle(typeid(*p).name(), 0, 0, 0) << endl;
cout << "a type:"<<abi::__cxa_demangle(typeid(a).name(), 0, 0, 0) << endl;

------------
p type:int (*) [3]
*p type:int [3]
a type:int [3]

p表示指向包含三个int型变量数组的指针,其存放的是==地址==,因此其占用空间大小根据机器是32位还是64位有所不同,我电脑是64位的,因此其占用空间为8字节。

*p可以认为表示的是包含三个int型变量的数组,因此其占用空间为3*4=12字节,表示的就是整型数组a

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
cout << "数组a地址:" << a << "\n"
<< "a[0]地址:" << &a[0] << "\n"
<< "a[1]地址:" << &a[1] << "\n"
<< "a[2]地址:" << &a[2] << endl;
cout << "*p =" << *p << "\n"
<< "*p + 1 =" << *p + 1 << "\n"
<< "*p + 2 =" << *p + 2 << endl;
cout << "*(*p)=" << *(*p) << "\n"
<< "*(*p + 1)=" << *(*p + 1) << "\n"
<< "*(*p + 2)=" << *(*p + 2) << endl;

/*--------下面储存地址与数组无法对应-----------*/
cout << "p =" << p << "\n"
<< "p + 1 =" << p + 1 << "\n"
<< "p + 1 =" << p + 2 << endl;
cout << "*p =" << *p << "\n"
<< "*(p + 1)=" << *(p + 1) << "\n"
<< "*(p + 2)=" << *(p + 2) << endl;
cout << "**p =" << **p << "\n"
<< "**(p + 1)=" << **(p + 1) << "\n"
<< "**(p + 2)=" << **(p + 2) << endl;
cout << "p[0]=" << p[0] << "\n"
<< "p[1]=" << p[1] << "\n"
<< "p[2]=" << p[2] << endl;

//--------------------结果----------------
sizeof(int)=4

数组a地址:0x61fdf0
a[0]地址:0x61fdf0
a[1]地址:0x61fdf4
a[2]地址:0x61fdf8
*p =0x61fdf0
*p + 1 =0x61fdf4
*p + 2 =0x61fdf8
*(*p)=4
*(*p + 1)=5
*(*p + 2)=6
/*--------下面储存地址与数组无法对应-----------*/
p =0x61fdf0
p + 1 =0x61fdfc
p + 1 =0x61fe08
*p =0x61fdf0
*(p + 1)=0x61fdfc
*(p + 2)=0x61fe08
**p =4
**(p + 1)=0
**(p + 2)=6422000
p[0]=0x61fdf0
p[1]=0x61fdfc
p[2]=0x61fe08

个人理解

  • p指向数组a的地址(首地址)即a[0]也即4的地址,p+1指向数组c的地址(首地址)即将该指针往后移动length字节,lengthp指向数组的所有元素的字节数;这里我先定义数组c,再定义数组a使得p+1指向数组c(理论上可用p+1指针访问数组c,但实践中不能这样访问)

  • p指向数组a的地址(p可以理解为指向二维数组的指针,但这里实际上指向一维数组),*p指向数组a第一个元素的地址,*p+1指向数组a第二个元素的地址,**p则表示提领*p地址的元素值。

    • 对于二维数组arr来说,p指向数组arr[0]{1,6,9,4}的地址,p+1指向数组arr[1]{5,6,7,8}的地址,以此类推。

    • *p表示长度为4的整型数组,直接输出*p即相当于输出其地址,使用**p提领该*p地址的值

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      int arr[3][4] = {{1, 6, 9, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}};
      int(*p)[4];
      p=arr;
      cout << &arr[0] << " " << &arr[1] << " " << &arr[2] << endl;
      cout << arr[0] << " " << arr[1] << " " << arr[2] << endl;

      ------------
      0x7ffc3814ec30 0x7ffc3814ec40 0x7ffc3814ec50
      0x7ffc3814ec30 0x7ffc3814ec40 0x7ffc3814ec50
      ------------
      arr type:int [3][4]
      arr[0] type:int [4]
      &arr[0] type:int (*) [4]
      p type:int (*) [4]
      *p type:int [4]
      **p type:int
      • 对于arr[0]&arr[0]来说,其类似于*pp,因此直接输出两者值一样
  • 由于*p+n可以代表数组a的元素的地址,n从0开始,因此使用*p+1*p+2依次访问a[1]a[2]的地址,使用*(*p+1)即a[1]*(*p+2)即a[2]访问对应的数据。若此时p指向二维数组arr,则p+1指向数组arr第二行的首地址即arr[1]也即&arr[1]*(p+1)表示该数组第二行第一个元素地址,*(p+1)+1表示该数组第二行第二个元素地址,依次类推。
对于二维整型数组array[i][j]来说,定义数组指针int (*p)[j]来指向该数组arrayp=array,则有如下结论:
  • p指向该数组第一行,p+1指向数组第二行,…,p+k指向数组第k+1行,依此类推;
  • *p指向该数组第一行第一列元素,*p+1指向该数组第一行第二列元素,…,*(p+k)+m指向该数组第k+1行第m+1列元素
  • *p表示该数组第一行第一列的元素即array[0][0]*(*p+1)指向该数组第一行第二列的元素即array[0][1],…,*(*(p+k)+m)表示该数组第k+1行第m+1列的元素即array[k][m]

数组指针指向一/二维数组

  • 一维数组赋值给数组指针
1
2
3
4
5
6
7
8
9
int a[3]={4,5,6};
int (*p)[3];
p=&a; //正确
p=a; //错误

-----
a type:int [3]
&a type:int (*) [3]
p type:int (*) [3]

错误原因

1
2
3
test.cpp:66:6: error: cannot convert 'int [3]' to 'int (*)[3]' in assignment
p = a;
^

这是因为&a 是指整个数组的首地址,而a是指数组首元素的首地址,虽然二者值相同的,但表示的意义不同。这里p是数组指针,指向整个数组。p=&a等号两边的数据类型完全一致,而p=a等号两边的数据类型不一致,因此会提示错误信息。

==补充:a表示数组名称,&a表示整个数组的首地址,类型为int (*) [3]a+1表示数组的第二个元素地址,类型为int*&a[0]表示数组首元素地址,类型为int*a[0]表示数组首元素值,类型为int==

  • 二维数组赋值给数组指针
1
2
3
4
5
6
7
8
9
int arr[3][4] = {{1, 6, 9, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}};
int(*p)[4];
p = arr; //正确
p = &arr; //错误

-------
arr type:int [3][4]
&arr type:int (*) [3][4]
p type:int (*) [4]

错误原因

1
2
3
4
5
array2.cpp:10:6: error: cannot convert ‘int (*)[3][4]’ to ‘int (*)[4]’ in assignment
10 | p = &arr;
| ^~~~
| |
| int (*)[3][4]

这里错误的原因同一维数组

2.指针数组

  • 存放一维数组元素的地址
1
2
3
4
5
6
7
8
9
10
11
int *p[4];	//定义数组指针
int a[4] = {1, 2, 3, 4};
// p[0] = &a[0];
// p[1] = &a[1];
// p[2] = &a[2];
// p[3] = &a[3];
//等价于下面语句
*p = &a[0];//指针数组赋值
*(p + 1) = &a[1];
*(p + 2) = &a[2];
*(p + 3) = &a[3];

这里给指针数组赋值不能只写*p=&a[0],否则后面利用它来访问数组的时候会出问题,因为没有把数组a的其他元素地址赋值给指针数组p的其他元素。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
cout << "&a[0]=" << &a[0] << endl;
cout << "&a[1]=" << &a[1] << endl;
cout << "&a[2]=" << &a[2] << endl;
cout << "&a[3]=" << &a[3] << endl;

cout << "&p[0]=" << &p[0] << endl;
cout << "&p[1]=" << &p[1] << endl;
cout << "&p[2]=" << &p[2] << endl;
cout << "&p[3]=" << &p[3] << endl;
cout << "p[0]=" << p[0] << endl;
cout << "p[1]=" << p[1] << endl;
cout << "p[2]=" << p[2] << endl;
cout << "p[3]=" << p[3] << endl;
//--------------
cout << "*p=" << *p << endl;
cout << "*(p+1)=" << *(p+1) << endl;
cout << "*(p+2)=" << *(p+2) << endl;
cout << "*(p+3)=" << *(p+3) << endl;
//-------other-----
cout << "p=" << p << endl;
cout << "p+1=" << p+1 << endl;
cout << "p+2=" << p+2 << endl;
cout << "p+3=" << p+3 << endl;

---------------
//p type:int* [4]
//*p type:int*
//sizeof(p)=32
//sizeof(*p)=8
&a[0]=0x61fdd0
&a[1]=0x61fdd4
&a[2]=0x61fdd8
&a[3]=0x61fddc

&p[0]=0x61fde0
&p[1]=0x61fde8
&p[2]=0x61fdf0
&p[3]=0x61fdf8
p[0]=0x61fdd0
p[1]=0x61fdd4
p[2]=0x61fdd8
p[3]=0x61fddc
//-------------
*p=0x61fdd0
*(p+1)=0x61fdd4
*(p+2)=0x61fdd8
*(p+3)=0x61fddc
//---other-----
p=0x61fde0
p+1=0x61fde8
p+2=0x61fdf0
p+3=0x61fdf8

这里p是指针数组,类型为int* [4],其中存放的是一维数组a的各个元素的地址,*p是指针数组中的指针,类型为int*,其存放的是一维数组a中一个元素的地址。

对于指针数组p&p[0]、&p[1]、&p[2]、&p[3]是指针数组p自身的地址,用于存放数组a的地址;p[0]、p[1]、p[2]、p[3]存放的是数组a{1,2,3,4}对应元素的地址。

使用指针数组p来访问数组a时,p[i]*(p+i)都是取数组a某个元素的地址,*p[i]*(*p+i)都是取数组a某个元素的值。

  • 存放二维数组元素的地址
1
2
3
4
5
6
7
8
9
10
11
12
int i;
int *p[4];
int b[4][2] = {{1, 3}, {2, 4}, {5, 7}, {6, 8}};
// p[0] = b[0];
// p[1] = b[1];
// p[2] = b[2];
// p[3] = b[3];
//等价于下面语句
*p = b[0];
*(p + 1) = b[1];
*(p + 2) = b[2];
*(p + 3) = b[3];
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
cout << &b[0] << " " << &b[1] << " " << &b[2] << " " << &b[3] << endl;
cout << b[0] << " " << b[1] << " " << b[2] << " " << b[3] << endl;
cout << b << " " << b+1 << " " << b+2 << " " << b+3 << endl;
cout << "&b[1] type:"<<abi::__cxa_demangle(typeid(&b[1]).name(), 0, 0, 0) << endl;
cout << "b[1] type:"<<abi::__cxa_demangle(typeid(b[1]).name(), 0, 0, 0) << endl;
cout << "b+1 type:"<<abi::__cxa_demangle(typeid(b+1).name(), 0, 0, 0) << endl;
cout << "p type:"<<abi::__cxa_demangle(typeid(p).name(), 0, 0, 0) << endl;
cout << "*p type:"<<abi::__cxa_demangle(typeid(*p).name(), 0, 0, 0) << endl;
cout << "**p type:"<<abi::__cxa_demangle(typeid(**p).name(), 0, 0, 0) << endl;
cout << "p[0] type:"<<abi::__cxa_demangle(typeid(p[0]).name(), 0, 0, 0) << endl;

----------------
0x61fdd0 0x61fdd8 0x61fde0 0x61fde8
0x61fdd0 0x61fdd8 0x61fde0 0x61fde8
0x61fdd0 0x61fdd8 0x61fde0 0x61fde8
&b[1] type:int (*) [2]
b[1] type:int [2]
b+1 type:int (*) [2]
p type:int* [4]
*p type:int*
**p type:int
p[0] type:int*

对于二维数组bb+1、b[1]、&b[1]的值都相同,但是其表示的意义不同;b+i&b[i]表示二维数组第i+1行数组(这里是一维数组)的首地址,b[i]表示二维数组第i+1行数组(一维数组)的第一个元素的首地址

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
cout << "&p[0]=" << &p[0] << endl;
cout << "&p[1]=" << &p[1] << endl;
cout << "&p[2]=" << &p[2] << endl;
cout << "&p[3]=" << &p[3] << endl;
cout << "p=" << p << endl;
cout << "p+1=" << p+1 << endl;
cout << "p+2=" << p+2 << endl;
cout << "p+3=" << p+3 << endl;

cout << "*p=" << *p << endl;
cout << "*(p+1)=" << *(p+1) << endl;
cout << "*(p+2)=" << *(p+2) << endl;
cout << "*(p+3)=" << *(p+3) << endl;

cout << "*(*p)=" << *(*p) << " "
<< "*(*p+1)=" << *(*p + 1) << endl;
cout << "*(*(p+1))=" << *(*(p + 1)) << " "
<< "*(*(p+1)+1)=" << *(*(p + 1) + 1) << endl;
cout << "*(*(p+2))=" << *(*(p + 2)) << " "
<< "*(*(p+2)+1)=" << *(*(p + 2) + 1) << endl;
cout << "*(*(p+3))=" << *(*(p + 3)) << " "
<< "*(*(p+3)+1)=" << *(*(p + 3) + 1) << endl;

--------
&p[0]=0x61fe00
&p[1]=0x61fe08
&p[2]=0x61fe10
&p[3]=0x61fe18
p=0x61fe00
p+1=0x61fe08
p+2=0x61fe10
p+3=0x61fe18
*p=0x61fde0
*(p+1)=0x61fde8
*(p+2)=0x61fdf0
*(p+3)=0x61fdf8
*(*p)=1 *(*p+1)=3
*(*(p+1))=2 *(*(p+1)+1)=4
*(*(p+2))=5 *(*(p+2)+1)=7
*(*(p+3))=6 *(*(p+3)+1)=8

这里和数组指针访问二维数组基本一致

值得注意的是,用数组指针和指针数组同时指向二维数组时,其引用和使用数组名引用都是一样的

表示数组中ij列的一个元素,下面的表示都是等价的:

*(*(p+i)+j)、*(p[i]+j)、(*(p+i))[j]和p[i][j]

3.二维数组相关

总结指针在数组中的一些应用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
int array[3][3]={{1,3,6},{2,5,8},{5,10,100}};

cout << "array type:"<<abi::__cxa_demangle(typeid(array).name(), 0, 0, 0) << endl;
cout << "array[0] type:"<<abi::__cxa_demangle(typeid(array[0]).name(), 0, 0, 0) << endl;
cout << "&array[0] type:"<<abi::__cxa_demangle(typeid(&array[0]).name(), 0, 0, 0) << endl;
cout << "&array[0][0] type:"<<abi::__cxa_demangle(typeid(&array[0][0]).name(), 0, 0, 0) << endl;
cout << "array+1 type:"<<abi::__cxa_demangle(typeid(array+1).name(), 0, 0, 0) << endl;
cout << "*array type:"<<abi::__cxa_demangle(typeid(*array).name(), 0, 0, 0) << endl;
cout << "&(*array) type:"<<abi::__cxa_demangle(typeid(&(*array)).name(), 0, 0, 0) << endl;
cout << "*&(*array) type:"<<abi::__cxa_demangle(typeid(*&(*array)).name(), 0, 0, 0) << endl;

----------------------
array type:int [3][3]
array[0] type:int [3]
&array[0] type:int (*) [3]
&array[0][0] type:int*
array+1 type:int (*) [3]
*array type:int [3]
&(*array) type:int (*) [3]
*&(*array) type:int [3]

从上面的输出可以看出,array是数组名,同时也可以说成是指向array[0][0]地址的指针,此时,array/array[0]/&array[0]/&array[0][0]的值是一样的,都是指向数组最开始的地址,但它们表示的含义是不同的。

从类型结果来看,array/array+1/array+2分别是该数组每一行整个一维数组的地址,等同于&array[0]/&array[1]/&array[2]*array/*(array+1)/*(array+2)分别是该数组每一行的首地址,等同于array[0]/array[1]/array[2]

==相当于C语言直接将相邻的方括号[]使用*来表示,比如说a[1]和1[a],最后展开后是*(a+1)*(1+a),本质上都是相同的==;

还有值得注意的就是,&*的操作是互逆的,这里&是取地址操作符,这里*是指针运算符,也能说是取内容运算符,相当于找到该指针指向变量的值。

参考链接:

数组指针和指针数组

数组指针和指针数组的区别