EasyC30,函数指针进阶
大家好,我是梁唐。
这是EasyC++系列的第30篇,继续来聊聊函数指针。
想要追求更好阅读体验的同学,可以访问github仓库: EasyLeetCode 。 函数指针进阶
简单的函数指针比较简单,但对于复杂的情况则显得有些恐怖。下面我们来看下C++ primer当中提供的一些例子: const double* f1(const double ar[], int n); const double* f2(const double [], int); const double* f3(const double *, int);
这三个函数看起来长得不一样,但其实是等价的。因为在函数参数列表当中,数组和指针是等价的。其次我们可以在函数的原型中省略掉变量名,因此 const double ar[] 可以简化成 const double [] ,也可以写成 const double * 。
有了这三个函数之后,假设我们要声明一个指针,指向这三个函数。根据我们前文当中说过的,可以将函数名替换成 (*pt) 来实现: const double* (*pt)(const double *, int) = f1;
其实这个语句看起来就有些复杂了,整个语句的可读性很差。如果不是知道这里用的是一个函数指针,乍一看想要看明白估计不太容易。我们可以分成两个部分来理解,其中 const double * 是一个整体,表示函数的返回值类型是一个 const double * 也就是一个常量浮点数的地址。其次 (*pt) 是一个整体,代替了函数名,表示这是一个指向函数的指针。
在C++11当中提供了叫做 auto 的新特性,它可以帮助变量自动识别对应的类型,可以解决一些类型特别复杂的问题,比如: auto p2 = f2;
函数指针有两种调用方法,除了可以使用 (*p2) 的方式调用之外,也可以直接使用名称调用: const double* x = p2(ar, 3); const double* y = (*p2)(ar, 3);
显然前者更好,更清楚。这里其实有一个疑问,为什么这两种方式都可以执行呢?这是因为当我们执行 auto p2 = f2 的时候,其实是执行的 auto p2 = &f2 ,C++会隐式地将函数转换成函数的地址。因为函数的值本身就是一个地址,所以这两种方式才都能正确地运行。
问题还没有结束,假如我们要定义一个指向函数的指针数组呢?这应该怎么声明?
也就是 const double* (*pt)(const double *, int) 这样一个类型的数组,它应该怎么声明,这个方括号应该放在那里?
正确答案是放在括号里: const double* (*pt[3])(const double *, int);
因为运算符 [] 的优先级高于 * ,因此 *pt[3] 表示 pt 是一个长度为3的指针数组。其他的内容表明了该指针的类型。
由于我们定义的是一个数组,所以这里不能使用 auto ,因为自动类型推断只能用于单值初始化而不能用于初始化列表。
到这里还没结束,还有更恐怖的,如果我们想要定义一个指向这个数组的指针,应该怎么办呢?如果使用 auto 可以写成: auto ptr = &pt;
如果不使用 auto 呢?首先我们可以想到,这个声明是基于 pt 的,我们需要在 pt 的声明上加上一个 * ,但问题是加在哪里呢?
进一步分析,会发现我们需要指出这是一个指针,而不是数组。意味着核心的部分应该写成 (*ptr)[3] ,表示这是一个指向长度为3的数组的指针。因为 [] 的优先级更高,所以需要使用括号。如果写成 *ptr[3] 表示这是长度为3的指针数组。
我们进一步倒推, (*ptr)[3] 这个数组当中的元素是什么类型呢?是指向函数的指针,所以写出来结果是这样: const double *(*(*ptr)[3])(const double*, int) = &pt;
很明显,这样的定义非常非常的难以理解。而且这还不是最复杂的情况,比如函数的返回类型又是一个指向一个函数的指针……明摆着告诉我们含义我们仍然要推敲一会,如果在一段不明的代码当中遇到,可能会直接抓狂吧……
也正因此,C++11当中推出了 auto 特性,可以简化这种情况。
多说一句题外话,golang语言当中将变量的类型放在变量的后面而不是前面,其中一个原因就是为了解决类似情况的复杂性。
如果是golang来定义同样的内容,会是这样的: func f2(arr []float64, n int) *float64 { // todo } // 函数指针 var p1 func([]float64, int) *float64 = f2; // 函数指针数组 var pt [3]func([]float64, int) *float64; // 函数指针数组的指针 var ptr *[3]func([]float64, int) *float64 = &pt;
很明显,虽然变量类型写在变量后面刚开始会不太习惯,但是很明显这样要清晰很多。
升级miui12。5增强版以后发布会说的增强版期待的头发都白了,结果升级系统以后使用了七八天时间,真的感觉不如一台2000的红米。说说我感觉是问题的问题,1电池超过40度马上变成60帧。2返回桌面,点开应用,多
继续搞圈钱的老套路。iPhone13还真的香吗?疫情期间,每个用FACEID解锁手机的小伙伴都会有同样的烦恼。就是每次手机解锁都要摘下口罩才能识别。(图源网络,侵删)最近据消息人士JonProsser爆料称,下个月发布大家心心念
屏下摄像头120Hz屏骁龙870,12GB256GB版跌至2998元目前市面上只有3款屏下摄像头手机,全球首发屏下摄像头手机是中兴Axon20,第二款是中兴Axon30,第三款才是小米MIX4。不过小米MIX4虽然高端,但是价格不便宜,发布价是49
手机厂商继续喊缺芯!各种被唱衰的苹果,却正在称霸5G时代?缺芯状况虽然有结构性缓解,但元部件的涨价情形实际上仍在持续。伴随下半年行业旺季的来袭,这将对5G手机价位段的进一步普及化,带来一定挑战。目前来看,进入2021年下半年,手机厂商发布
同是骁龙778G,这3款5G手机,哪款更值得入手?骁龙778G是高通一款优化还不错的手机芯片,采用了台积电6nm工艺制程,采用134架构,有1个2。4GHzCortexA78超大核,3个2。2GHzA78大核,以及4个1。9GHz
八月快结束了!盘点八月降价最多的手机(上)第一款iQOO7,现在某多多百亿补贴后基本上2900元左右就可以买得到了。骁龙888处理器,6。62英寸三星E3材质AMOLED直屏,支持120Hz刷新率。后置4800万像素三摄,
三星部分GalaxyWatch4发货日期推迟到9月中旬三星GalaxyWatch4和三星GalaxyWatch4Classic已经于8月11日开始提供预购。所有GalaxyWatch4设备的发货日期为2021年8月27日。8月1126
变天了!阿里巴巴未来会怎样?时代变了,对手变了,一切都变了。最近,互联网公司财报密集发布,京东拼多多快手轮番上场,数据喜忧参半。相比而言,阿里巴巴财报中,营收月活跃用户数(MAU)年度购买用户数净利润等更符合
超八成受访者经历电商砍单较2019年不降反升北京商报讯(记者赵述评蔺雨葳)8月27日,北京市消费者协会(以下简称北京市消协)发布互联网消费砍单问题调查结果。调查发现,在本次调查涉及的40个电商平台中,有17个电商平台涉嫌存在
特斯拉澳洲储能站爆炸的元凶找到了韩非子云千丈之堤,以蝼蚁之穴溃百尺之室,以突隙之烟焚,警醒庞大工程的管理者关注细微处的隐患。其中,消防安全隐患最容易让储能系统翻车,连被神化的特斯拉也不能幸免。特斯拉储能项目突发超
我国移动网速全球排第四我国移动网速全球排第四我国固网网速全球排第18位27日,中国互联网络信息中心发布的第48次中国互联网络发展状况统计报告显示,截至2021年4月,我国光纤宽带用户占比达94,千兆光网