下载安卓APP箭头
箭头给我发消息

客服QQ:3315713922

C++的复杂声明其实并不复杂!

作者:课课家教育     来源: http://www.kokojia.com点击数:757发布时间: 2018-11-19 16:06:44

标签: c++入门c语言与c十十的区别c语言

大神带你学编程,欢迎选课

  课课家:C++ 的复杂声明其实并没有想象中的那么复杂,下面就来了解了解。

     在学习C系列语言的过程之中,理解C/C++的复杂声明一直是初学者很困扰的问题。笔者初学之时也深受困扰,对很多规则死记硬背。后续在阅读《C专家编程》之后,尝试在编译器的角度来理解C/C++的声明解析,并且编写代码将这部分逻辑串联起来,之后再看到许多看似复杂的声明,也能够很好的理解和消化了。

  1.复杂的声明

  在编写C/C++代码时偶尔能看到如下的复杂声明:float(*(*e[10])(int*))[5]。我想你的第一反应一定是: C ++程序设计MMP。虽然我们在实际工作之中是很少出现这种极其复杂的声明逻辑,同时也不提倡使用这样的声明。但是学会理解和解析这类复杂的声明逻辑,可以更好的理解C/C++之中诸个关键词是如何进行组织,来表达逻辑数据结构的,也能更好的理解各个关键词的使用方式。

  比如之前笔者写的一篇文章之中整理了C/C++之中const关键词的用法 《C++雾中风景3:const用法的小结》的之中通过口诀的方式记忆const关键字在声明之中的先后顺序来厘清不同的逻辑。这种方式不仅效率低下,而且并没有理解到为什么不同的先后顺序会对声明逻辑产生影响。在本篇文章之中,笔者尝试带大家忘记这些口诀,从编译器的角度去理解编译器是如何处理这些声明的逻辑,知其然而知其所以然。

  2.优先级规则

  C/C++的声明模型是及其晦涩的,笔者简单统计了涉及声明模型的关键字如const,volatile等大概有十个左右。更为复杂的是在C/C++之中这些关键字的先后顺序与括号可以任意组合并且发生看起来很奇妙的"化学反应"。

  万变而不离其中,总结出规律之后,再复杂的模型也可以简化成我们可以理解的单元来处理。所以我们先来看看C/C++声明的优先级规则。

  声明是由标识符,也就是它的名字开始解析的。

  获取了声明之后,接下来安装如下优先级别来依次处理声明:

  1. 优先处理括号部分的声明逻辑。

  2. 优先处理后缀操作符,如[],()

  3. 处理前缀操作符,如*,const

  后续可以依次从右往左处理之前的声明了。

  掌握了上述的优先级规则之后,我们回到本文一开始举的一个小栗子

  1.找到声明e,e将作为声明的名字。

  2.处理后缀操作符,也就是e代表的是一个容量为10的数组。

  3.回到前缀操作符,该数组存储的内容为指针。

  4.跳出括号,开始新的一轮的**优先级规则**,处理后缀操作符(),我们

  发现这个指针指向的是一个参数为int\\*的函数。

  5.接着再次回到前缀操作符,所以这个函数返回值依然是一个指针。

  6.跳出括号,继续前文的逻辑,我们发现该指针指向了一个内容为cocos2dxfloat,容量为5的数组。

  通过上述栗子我们不难发现,对于声明的处理本质上是一个有限自动机的状态变化过程,所以编译器同样也是按照上述的规律来理解并处理程序的复杂声明的。了解了优先级规则,我们也就不难去实现一个简单的小程序**cdecl**来处理声明逻辑了。

  ####3.简单的代码实现

  通过上述流程的说明,我们很容易想到可以用**栈**来保存声明标识符左边的内容,而名字右边的内容则依照优先级规则依次处理。(优先处理数组与函数)。

  * **先分类将要处理声明的种类,并且声明token类型来进行处理**

  enum type_tag {IDENTIFIER,QUALIFIER,TYPE,POINTER,LPAREN, LBRACKET,RPAREN,RBRACKET};

  struct token {

  type_tag type;

  string content;

  };

  * **不断读取token,并且压入栈中,直到读取到声明标识符**

  void read_to_first_identifer() {

  gettoken();

  while (this_t.type != IDENTIFIER) {

  token_stack.push(this_t);

  gettoken();

  }

  cout << this_t.content + " is ";

  gettoken();

  }

  * **按照优先级法则处理逻辑,先右后左,遇到括号弹出之后继续上述逻辑**

  void deal_with_declarator(){

  switch (this_t.type) {

  case LBRACKET:deal_with_arrays();break;

  case LPAREN:deal_with_function_args();

  }

  deal_with_pointers();

  while(!token_stack.empty()) {

  if(token_stack.top().type == LPAREN) {

  token_stack.pop();

  gettoken();

  deal_with_declarator();

  } else {

  cout << token_stack.top().content + " ";

  token_stack.pop();

  }

  }

  }

  * **处理数组类型的函数**

  void deal_with_arrays() {

  while (this_t.type == LBRACKET) {

  cout << "array ";

  gettoken();

  if(isdigit(this_t.content[0])) {

  printf("0....%d of ",atoi(this_t.content.c_str()) - 1);

  gettoken();

  }

  gettoken();

  }

  }

  * **处理函数类型的函数**

  void deal_with_function_args() {

  while(this_t.type != RPAREN) {

  gettoken();

  }

  gettoken();

  cout << "function returning ";

  }

  ```

    小编:刚开始学C语言,关于这些复杂声明一类的我也是烦透了,难学难懂,但后来发现其实学习编程一类的知识,多练习多总结真的很重要!以上就是为大家找的关于C语言一类的复杂声明相关知识,想学习更多内容,请点击课课家提供的相关链接噢。

赞(14)
踩(0)
分享到:
华为认证网络工程师 HCIE直播课视频教程