上篇文章我们对指针进行了深入了解,接下来我们继续学习指针。
一、二级指针
前面我们学习了指针变量,我们知道只要是变量就会有地址,那么指针变量的地址又存放在哪呢?下面我就要介绍如何存放指针变量的地址——二级指针
我们先来看一段代码:
我们可以看到变量ppa存的是变量pa的地址:
前面我们对 int* 进行了理解,现在我们再对 int* * 进行理解一下:
向其他的类型也是一样的如float* *,char* *等等。
在理解二级指针后,我们在学习一下二级指针的运算。
我们在学习一级指针的时候对pa解引用找到的就是a。*pa=a
那么*ppa 通过对ppa中的地址进⾏解引⽤,这样找到的是 pa , *ppa 其实访问的就是 pa。
**ppa 先通过 *ppa 找到 pa ,然后对 pa 进⾏解引⽤操作: *pa ,那找到的是 a。
当然二级指针的运算也和一级指针相似,除了二级指针还有三级指针,四级指针等等,不过用的非常少。
二、指针数组
下面我们学习学习一个新知识——指针数组
1.什么是指针数组
我们先来思考一个问题,指针数组是指针还是数组?
我们不妨来类比一下,整型数组,是存放整型的数组,字符数组是存放字符的数组。 那指针数组呢?是存放指针的数组。注意:指针数组是数组不是指针!
我们知道整型数组存放的是整型数据,字符数组存放的是字符数据,那么指针数组存放的就是指针(数组中的每个元素是指针类型),也就是说指针数组的每个元素都是⽤来存放地址(指针)的。
那么指针数组是怎么表示的呢?我们只需要把数组名前面的类型定义为指针类型就可以啦!
如:int* arr1[5] float* arr2[10]
指针数组的每个元素是地址,⼜可以指向⼀块区域。
2.指针数组模拟二维数组
在学习完指针数组后我们就可以用其模拟一下二维数组啦,不过模拟出的是二维数组的效果,并不是真的二维数组。我们先来看代码:
我们可以看到上段代码输出的效果和二维数组是一样的,arr[i]是访问arr数组的元素,arr[i]找到的数组元素指向了整型⼀维数组,arr[i][j]就是整型⼀维数组中的元素。也就是说int*arr[3]中存放的元素都是数组,我们有知道数组名是数组首元素的地址,所以又通过对每个数组进行解引用找到每个数组中的元素。
上述的代码模拟出⼆维数组的效果,实际上并⾮完全是⼆维数组,因为每⼀⾏并⾮是连续的。
三、字符指针变量
在之前的学习中我们知道有⼀种指针类型为字符指针 char*,它的用法我相信大家都比较熟悉了,下面我个大家介绍另外一种使用方式,我们先来看代码:
这里我们看到好像是把字符串abcdef,放进了字符指针pa中,而且这里也并没有解引用,但其实这里本质是把字符串abcdef的首字符的地址放在了pa中。
在这里我们可以把字符串想象成一个字符数组,但是这个数组是不能被修改的,我们称作常量字符串,当它出现在表达式中时,它的值是第一个字符的地址。
好了,在介绍完这些后我们来看一段有趣的代码:这⾥str3和str4指向的是⼀个同⼀个常量字符串。C/C++会把常量字符串存储到单独的⼀个内存区域, 当⼏个指针指向同⼀个字符串的时候,他们实际会指向同⼀块内存。但是⽤相同的常量字符串去初始 化不同的数组的时候就会开辟出不同的内存块。所以str1和str2不同,str3和str4相同。
四、数组指针变量
1.什么是数组指针变量
之前我们学习了指针数组,我们知道它是一种数组,那么数组指针变量又是什么呢?我们不难猜出它其实是指针变量,不过它存放的是数组的地址,能够指向数组的指针变量。
那么它有是怎么表示的呢?
注意:arr2先和*结合,说明arr2是⼀个指针变量,然后指着指向的是⼀个⼤⼩为10个整型的数组。所以 arr2是⼀个指针,指向⼀个数组,叫数组指针。[]的优先级要⾼于*号的,所以必须加上()来保证arr2先和*结合。
2.数组指针变量的初始化
数组指针变量是⽤来存放数组地址的,那怎么获得数组的地址呢?就是我们之前学习的&数组名,如果要存放个数组的地址,就得存放在数组指针变量中
我们调试也能看到 &arr 和 pa 的类型是完全⼀致的。
3.数组指针的使用
上面我们了解了数组指针变量,那么该怎么用呢?我们直接来看代码:
这里pa==&arr, *pa==*&arr==arr (对pa解引用就等价于*&arr,&和*就等于抵消了,所以*pa找到的就是arr)。当然它的用法不仅仅这一种哦,我们继续往下看。
4.⼆维数组传参的本质
我们了解数组指针后,就可以了解⼆维数组传参的本质了。过去我们有⼀个⼆维数组的需要传参给⼀个函数的时候,我们是这样写的:
所以,根据数组名是数组⾸元素的地址这个规则,⼆维数组的数组名表⽰的就是第⼀⾏的地址,是⼀维数组的地址。根据上⾯的例⼦,第⼀⾏的⼀维数组的类型就是 int [5] ,所以第⼀⾏的地址的类 型就是数组指针类型 int(*)[5] 。那就意味着⼆维数组传参本质上也是传递了地址,传递的是第⼀⾏这个⼀维数组的地址,那么形参也是可以写成指针形式的。如下:
总结下来:⼆维数组传参,形参的部分可以写成数组,也可以写成指针形式。
五、函数指针变量
1.函数指针变量的创建
在函数指针变量的创建之前,我们先要了解什么是函数指针变量,根据前⾯学习整型指针,数组指针的时候,我们的类⽐关系,我们不难得出结论: 函数指针变量应该是⽤来存放函数地址的,未来通过地址能够调⽤函数的。
那么函数有没有地址呢?我们可以来测试一下:
我们可以看到确实打印出来了地址,所以函数是有地址的,函数名就是函数的地址,当然也可以通过 &函数名的⽅式获得函数的地址。如果我们要将函数的地址存放起来,就得创建函数指针变量,函数指针变量的写法其实和数组指针⾮常类似。如:
2.函数指针变量的使⽤
在了解函数指针变量的创建后,我们来学习怎么使用函数指针变量。使用·函数指针变量无非就是通过函数指针调⽤指针指向的函数。
是不是很简单。
3.回调函数
回调函数就是⼀个通过函数指针调⽤的函数。如果你把函数的指针(地址)作为参数传递给另⼀个函数,当这个指针被⽤来调⽤其所指向的函数 时,被调⽤的函数就是回调函数。回调函数不是由该函数的实现⽅直接调⽤,⽽是在特定的事件或条 件发⽣时由另外的⼀⽅调⽤的,⽤于对该事件或条件进⾏响应。我们先来写一个简易版的计算器代码:
上面代码中红色框出来的我们可以发现有许多代码都是一样的,这样就显得有些冗余,我们有没有办法简化代码呢?当然可以。因为红⾊框中的代码,只有调⽤函数的逻辑是有差异的,我们可以把调⽤的函数的地址以参数的形式传递过去,使⽤函数指针接收,函数指针指向什么函数就调⽤什么函数,这⾥其实使⽤的就是回调函数的功能。
我们可以中间一段代码改造一下:
我们来理解一下这段代码
以上就是函数回调的使用啦。
六、函数指针数组
1.函数指针数组的定义
前面我们学习了指针数组,它是存放指针的数组,而函数指针数组也是存放地址的数组,只不过它存放的是函数的地址。那么它又是怎么定义的呢?我们只用把函数指针和数组结合行了。如:
int (*parr[10]) ()
在这里parr先和 [] 结合,说明 parr是数组,数组的内容是 int (*)() 类型的函数指针。
下面我们就来学习一下它怎么用。
2.转移表
我们还是用上面的计算器代码来举例:
我们再来改造一下这个代码
下面我们就解释一下这段代码:
上面代码当input=1时,就找到函数指针数组的第二个元素(因为第一个元素存的是空指针)也就是add函数的地址,对其进行解引用找到的就是add函数然后去调用这个函数。这种方式就叫做转移表。
好了,以上(以及前两篇文章)就是指针的所有内容了,感谢大家的观看,希望能给大家带来帮助,如果哪里有不对的地方,感谢指正(您的指正对我就是莫大的帮助!)。