前面已经写过关于C/C++一级指针的内容,一级指针对初学者来说就已经比较难以理解了,二级指针就更加难了,本文借助数组来理解二级指针;那么,下面就让我们一起来学习一下二级指针和二维数组;
两个与指针息息相关的运算符
说起指针,那么有两运算符兄弟不得不提,取址运算符“&”和间接运算符 “*”(indirection operator),有时候这会被称为解引用运算符;
取址运算符“&”:用来取得其操作数的地址;
间接运算符 “*”:它的操作数必须是指针类型,用来取得指针所指向的对象;
例如:
int a = 100 ;
&a ;
此处&a其实就是一个指针了, 它的类型是int类型指针类型(int*类型):
int a = 100 ;
&a ;
int* p = &a;
int* p2 = nullptr ;
p2 = &a ;
int b = 200 ;
*p2 = b ;
cout<<* p;
在C/C++中赋值运算符“=”左右两边类型必须相同或者存在继承关系,p2 = &a ;说明,p2就等于对象a的地址,我们通过间接运算符 “*”或者称解引用运算符更易理解,就能取得p2所存地址上存放的对象。
此时若运行上述代码,cout<<* p;输出的将是200 ;这是因为p和p2都存的a的地址,但是*p2 = b将该地址上的对象的值修改为200了,所以当你再用*p去取值得时候与*p2访问的是同一块地址;
指针内存示意图
二级指针
有了一级指针的基础,指针变量在计算机内存中其实存放的是指向的对象的地址,指针的功能其实都是通过取址运算符“&”,和间接运算符 “*”来实现的,指针本身是没什么神奇的。所以大家可以对其少一些敬畏之心。
int a = 100 ;
&a ;
int* p = &a;
int* p2 = nullptr ;
p2 = &a ;
int b = 200 ;
*p2 = b ;
int** p3 = &p2 ;
cout<<** p3;
二级指针示意图
二级指针变量存的其实就是一级指针变量在内存中的地址。再由解引用运算符“*”逐级剥离,最终取的实体对象。
二维数组与指针
指针与一级指针的关系很简单:一维数组,在内存上是连续的,所以找到一个数组的所有元素只需要知道该数组的元素类型和首元素的地址就可以逐一找到所有元素。而数组名就代表首地址;
一维数组示意图
int arr[] = {0,1,2,3,4,5} ;
int* pArr = nullptr ;
pArr = arr;
pArr = &arr[0] ;
cout<<*(pArr+1) ;//通过指针偏移即能访问后续元素
cout<<*(arr+1) ;
如上述代码:pArr = arr;与pArr = &arr[0] ;是一样的;cout<<*(pArr+1) ;与cout<<*(arr+1) ;是一样的,arr在此处亦可看成一个指针变量;
那么,二维数组就是在一维数组的纵向上再增加元素:
二维数组示意图
上述二维数组可以看成是由{01,02,03,04,05},{11,12,13,14,15},{21,22,23,24,25}三个数组组成。每个数组又可以用指针表示,那么二维数组就是指针组成的数组,所以用二级指针表示就是:
int arr[3][5] = {{01,02,03,04,05},
{11,12,13,14,15},
{21,22,23,24,25}} ;
int* pArr1 = arr[0] ; //等价int* pArr1 = &arr[0][0]
int* pArr2 = arr[1] ; //等价int* pArr1 = &arr[1][0]
int* pArr3 = arr[2] ; //等价int* pArr1 = &arr[2][0]
int* arrP[] = {pArr1,pArr2,pArr3};
int** pArr_level2 = arrP ;
每行(横向为行)首元素为该行数组的首地址:arr[0],arr[1],arr[2];
由于数组储存物理的连续性,所以可以将二维数组拉伸看做一维数组,可以由一级指针表示,再由指针偏移逐一访问各元素:int arr3[3][5] = {01,02,03,04,05,11,12,13,14,15,21,22,23,24,25} ;
int arr[3][5] = {01,02,03,04,05,11,12,13,14,15,21,22,23,24,25} ;
int* p = nullptr ;
for (p = arr[0]; p < arr[0]+15; p++)
{
cout<<*p<<endl; ;
}
指针数组
所以二级指针可以看成是由指针组成的一维数组