C++ 理解复杂声明

C和C++中的复杂声明往往难以理解,这里为大家总结出理解它们的系统化方法。

前置知识

1.声明

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

int fun(int a);

这个声明包含两个名字funa。然而只有fun是“主语”,也只有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])是表示函数的括号,去掉其中的名字ab,能得到等价的声明:

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

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

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

2.按优先级顺序,从“主语”逐步翻译

确定主语的位置后,我们按照优先级顺序,一个一个符号解释。

每个符号对应的译文如下表所示(可以按语境略改动,使之通顺):

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

译文以“主语 是一个...”开头,以 基本类型 结束。每解释一个符号,就将该符号的译文添加到当前译文末尾。

比如我们要翻译int (*(**A())[10])(double);这个语句,流程如下:

(为了清楚,我们将已经解释的符号划掉,将当前符号和译文用红色标记)

当前原文:int (*(**A())[10])(double);
当前译文:A是一个...

当前原文:int (*(**A())[10])(double);
当前译文: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)的函数,这个函数的返回值是一个...

当前原文:int (*(**A())[10])(double);
当前译文:A是一个
     形参列表为空的函数,这个函数的返回值是一个
     指针,这个指针指向一个
     指针,这个指针指向一个
     数组,这个数组的元素是10个
     指针,这些指针指向
     形参列表为(double)的函数,这个函数的返回值是一个
     int

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

转换成常见的定语前置句:A是一个形参列表为空、返回值为指向指向含有10个指向形参列表为(double)、返回int的函数的指针的数组的指针的指针 (别问我是什么意思。。。)

猜你喜欢

转载自www.cnblogs.com/BinarySong/p/12913504.html