C语言深入理解指针四(17)


前言

继续!


一、字符指针变量

  在指针的类型中我们知道有一种指针类型为字符指针char*
一般使用:
在这里插入图片描述
还有一种使用方式如下:
在这里插入图片描述
代码 const char* pstr = “hello bit.”; 特别容易令人误解为把字符串 hello bit. 放到字符指针 pstr 里面,但是本质是把字符串 hello bit. 首字符的地址放到 pstr 中

1.我们可以把字符串想象成一个字符数组,但是这个数组是不能被修改的
2.当常量字符串出现在表达式中的时候,它的值是第一个字符的地址,以下两种写法等价
在这里插入图片描述

有一道关于字符串相关的笔试题,如下:
在这里插入图片描述

请给我你认为的输出

答案是第一个是not,第二个是yes
在这里插入图片描述
我们来思考原因,首先下面那个字符串被const修饰,不会被修改,内容一样,从内存利用率来说,没有必要存多份,只存一份,大家公用即可,而第一个则不然

二、数组指针变量

数组指针变量是什么?

 指针数组:是数组,是存放指针的数组
 整型指针:是指针,指向整型的指针
 数组指针变量:是指针变量,用来存放数组的地址,是能够指向数组的指针变量

为了让你更好地区分,告诉我下面两个分别是什么
在这里插入图片描述
答案是前者是指针数组,后者是数组指针
更准确地说,p1是数组,数组10个元素,每个元素的类型是int*
p2是指针,是指向数组的指针,而所指向的数组有10个元素,每个元素的类型是int

有点绕,你再看看如下:
在这里插入图片描述
通过括号, * 先跟 parr 结合,代表 parr 是一个指针(必须要加括号, [ ] 的优先级大于 * )

数组指针变量怎么初始化

数组指针变量是用来存放数组地址的,那怎么获得数组的地址呢?其实就是我们之前所学的&数组名
在这里插入图片描述

如果要存放,那就得存放到数组指针变量当中,即:
int (*parr)[10] = &arr;

通过调试,我们也能看到 &arr 和 p的类型是完全一致的
在这里插入图片描述

三、二维数组传参的本质

  有了数组指针的理解,我们就能够讲以下二维数组传参的本质了
过去我们有一个二维数组需要传参给一个函数的时候,我们是这样写的:
在这里插入图片描述
  那二维数组可以写成指针吗?可以!
这需要我们再次理解一下二维数组,二维数组可以看做是每个元素是一维数组的数组,也就是二维数组的每个元素是一个一维数组,那么二维数组的首元素就是一行,是个一维数组
在这里插入图片描述
所以,根据数组名是数组首元素的地址这个规划,二维数组的数组名表示的就是第一行的地址,是一维数组的地址,根据上面的例子,第一行的一维数组的类型是int[5],所以第一行的地址的类型就是数组指针类型int (*p)[5],那就意味着二维数组传参本质上也是传递了地址,传递的是第一行在这个一维数组的地址,那么形参也是可以写成指针形式的,如下:
在这里插入图片描述

四、函数指针变量

函数指针变量的创建

  什么是函数指针变量呢?
还是类比,它还是个指针变量,是用来存放函数的地址的变量,未来能通过地址调用函数的
来个测试:
在这里插入图片描述
输出结果如下:
在这里插入图片描述
确实打印出来了地址,所以函数是有地址的,函数名就是函数的地址,当然也可以通过 &函数名 的方式获得函数的地址

跟数组区别的是,函数前面不管加不加取址符 & 都表示函数的地址
int (*pf)(int, int) = &Add; // pf就是函数指针变量
int ret1 = Add(4,9);
int ret2 = (*pf)(4,9); // 调用

函数指针变量的使用

在这里插入图片描述
如上,注意函数指针变量后面的括号加上x,y即(int x, int y)也是可以的

五、两段有趣的代码

代码1

(* ( void ( * )( ) ) 0 )( );
我们发现void(*)()是函数指针类型,而这个类型括号抱起来再跟上0,是强制类型转换,然后再调用
1.把0这个整数值,强制类型转换成一个函数的地址,这个函数没有参数,返回类型是void
2.去调用0地址处的函数!

此段代码出自《C陷阱与缺陷》:在这里插入图片描述

代码2

void (* signal(int, void(*)(int) ) )(int);

这段代码又是什么意思?哈哈,认真思考,这两句代码是本篇最难的部分了~

先来想signal是啥意思,这里没有传参,也没和 * 混在一起,说明它本身是个函数
而一个函数,参数有了,那么还差返回类型,我们把函数名和参数去掉,最后就发现剩下了void( * )(int)
所以,signal函数的参数有2个,第一个是int类型,第二个是函数指针类型,该指针指向的函数参数是int,返沪类型为void
signal函数的返回类型是这种类型的void(*)(int)函数指针
该指针指向的函数参数是int,返回类型是void

typedef关键字

typedef关键字是用来简化类型名的
比如说定义一个无符号整数num

unsigned int num; // 太麻烦

这时候,我们可以重命名

typedef unsigned int uint;
typedef int* ptr_t;
typedef int(* parr_t)[5]; // 简化指针类型int (*)[5]
注意,要修改数值指针类型和函数指针类型的时候,名字放里面

对于前面的void (* signal(int, void(*)(int) ) )(int);
我们把 void ( * )(int) 重命名一下
typedef void( * pf_t)(int);

所以上述代码可以简化为
pf_t signal(int, pf_t)(int);

六、函数指针数组

  如果我们要设计一个计算器,要求对整型进行四种运算,显然第一想到的是switch…case语句,可这样代码过于冗杂,我们可以尝试把四种运算都放在一个数组里面,这个数组就是函数指针数组
在这里插入图片描述
跟函数指针变量相比,就是在名字后面加上了[ ],我们成功的将函数的地址放进数组里,这意味着我们可以更加高效的管理函数了!


总结

  回调函数预告,我保证下一章你会感慨函数调用的奇妙!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值