C++ 理解复杂声明

C++ 理解复杂声明

本文转载于https://www.cnblogs.com/BinarySong/p/12913504.html
C和C++中的复杂声明往往难以理解,这里为大家总结出理解它们的系统化方法。

前置知识

1.声明

C++标准对声明的定义是:声明规定名字将如何被解释。一个声明只解释一个名字。这个名字是声明的“主语”,是描述的中心,其余的名字都并非重点。例如:

int fun(int a);

这个声明包含两个名字funa。然而只有fun是“主语”。后面的部分会阐述如何确定这唯一的“主语”。

2.基础(Fundamental)类型和衍生(Derived)类型

一个声明语句有且只有一个基础类型。基础类型写在声明的最左边。
以下语句有的声明了变量,有的声明了函数。但它们的基础类型都是int。

int *a;
int b[10];
int c();
int (&d(char (*)[10]))[10];

当然,同样基础类型的声明可以连写,中间用逗号隔开。简便起见,本文中的“一个声明”,都是指不连写的情况。

int *a, b[10], c(), (&d(char (*)[10]))[10];//与前例等价

“主语”被符号修饰后,形成衍生类型。衍生类型也可以衍生出新的衍生类型,例如指针、引用、函数等。

3.符号和优先级

声明中可能出现各种符号,例如:

  • i[] 表示数组
  • i() 表示函数
  • *i 表示指针
  • &i 表示引用
    和表达式中的运算符一样,声明中的符号也有优先级。规则十分简单:
  • “主语”右侧符号的优先级高于左侧符号
  • 有分组符号()的先解释分组符号内的
  • 注意,这里的 表示分组的() 和 表示函数的() 一定要区分开来。区分的方法很简单:
  • 空括号()一定表示函数
  • 包含类型的括号一定表示函数,如(int a)、(double,int)等。

分析声明语句
有了以上前置知识,我们现在来研究如何把一个声明翻译成“人话”。

1.找“主语”

对于“主语”没有省略的声明来说,方法很简单,把所有函数符号()里的形参名字去掉,剩下的名字就是“主语”。
例如:

int *(*f)(int *a(), int (&b)[10]);

用前述方法可以看出,(int *a(), int (&b)[10])是表示函数的括号,去掉其中的名字a、b,能得到等价的声明:

int *(*f)(int *(), int (&)[10]);

显然,这里的f是“主语”。

对于“主语”省略的声明,首先按前述方法两种括号区分开,“主语”就隐含在最内层分组符号里、前缀符号(*、&)和后缀符号([]、())之间。
如声明:

int (&(*[5])())[5];

省略的“主语”S位置如下:

int (&(*S[5])())[5];
2.按优先级(先右后左)顺序,从“主语”逐步翻译

确定主语的位置后,我们从主语开始,按照先右后左的顺序,一个一个符号解释。每个符号对应的译文如下表所示(按语境略改动,使之通顺):

符号译文
*…指针,这个指针指向一个…
&…引用,这个引用引用一个…
[N]…数组,这个数组的元素是N个…
(形参列表)…形参为 形参列表 的函数,这个函数的返回值是…

译文以“主语 是一个…开头,以 基本类型 结束。每解释一个符号,就将该符号的对应的译文添加到当前译文末尾。
比如我们要翻译int (*(**A())[10])(double);这个声明,流程如下:
(为了清楚,我们将已经解释的符号划掉,将当前符号和译文用红色标记)

原文:int (*(**A())[10])(double);
译文:A是一个…
解释:从主语开始翻译。

原文:int (*(**A ())[10])(double);
译文:A是一个形参列表为空的函数,这个函数的返回值是一个…
解释:按先右后左的顺序,我们先翻译(),将其对应的译文添加到末尾。可以看出,即使只翻译第一步,我们就已经能知道A实际上是一个函数,可以对它有最基本的把握。之后的信息全部是关于A的返回值的。

原文:int (*(**A() )[10])(double);
译文:A是一个
     形参列表为空的函数,这个函数的返回值是一个
     指针,这个指针指向一个…
解释:右侧已经没有符号(右侧的“)”表示分组),我们翻译左侧的*,将其对应的译文添加到末尾。

原文:int (*(**A() )[10])(double);
译文:A是一个
     形参列表为空的函数,这个函数的返回值是一个
     指针,这个指针指向一个
     指针,这个指针指向一个…
解释:同上,我们翻译左侧的*,将其对应的译文添加到末尾。

原文:int (*(**A())[10])(double);
译文:A是一个
     形参列表为空的函数,这个函数的返回值是一个
     指针,这个指针指向一个
     指针,这个指针指向一个
     数组,这个数组的元素是10个…
解释:分组符号()内部翻译完成,划掉,优先翻译右侧的[]

原文:int (*(**A())[10] )(double);
译文:A是一个
     形参列表为空的函数,这个函数的返回值是一个
     指针,这个指针指向一个
     指针,这个指针指向一个
     数组,这个数组的元素是10个
     指针,这些指针指向…
解释:右侧已经没有符号(右侧的)表示分组),我们翻译左侧的*

原文:int (*(**A())[10])(double);
译文:A是一个
     形参列表为空的函数,这个函数的返回值是一个
     指针,这个指针指向一个
     指针,这个指针指向一个
     数组,这个数组的元素是10个
     指针,这些指针指向
     形参列表为(double)的函数,这个函数的返回值是一个…
解释:分组符号()内部翻译完成,划掉。优先翻译右侧表示函数的(double)

原文:int (*(**A())[10])(double) ;
译文:A是一个
     形参列表为空的函数,这个函数的返回值是一个
     指针,这个指针指向一个
     指针,这个指针指向一个
     数组,这个数组的元素是10个
     指针,这些指针指向
     形参列表为(double)的函数,这个函数的返回值是一个
     int
解释:最后以基础类型int结尾。

翻译结果:A是一个形参列表为空的函数,这个函数的返回值是一个指针,这个指针又指向一个指针,后者指向一个数组,这个数组的元素是10个指针,这些指针各自指向一个形参列表为(double)、返回int的函数。

虽然很复杂,但本质上就是一个函数,而这点我们从第一步就可以得出,这也是这个方法的优势。

如果你愿意,还可以把它转换成常见的定语前置句:

A是一个形参列表为空且返回值为指向指向含有10个指向形参列表为(double)且返回int的函数的指针的数组的指针的指针

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值