浅谈C语言变量声明的解析
C语言本身提供了一种不甚明确的变量声明方式——基于使用的声明,如int *a,本质上是声明了*a的类型为int,所以得到了a的类型为指向int的指针。对于简单类型,这样声明并不会对代码产生多大的阅读障碍,而对于复杂的声明,比如标准库的signal函数签名,void (*signal( int sig, void (*handler) (int))) (int),这是什么?一眼看不出来吧,这是一个函数,接受两个参数,一个int,一个函数指针,而这个函数指针指向的函数接受一个int并返回void;返回一个函数指针,这个函数指针指向的函数接受一个int并返回void。尽管很多人都吵着说工程中谁写出来这样的代码就炒他鱿鱼,但人家标准库的确是写出了这样的代码的,你怎么办?
解析这样的复杂声明,我之前见过一种方法——右旋转法,方法是这样的:
- 从变量名开始,先右再左地,交替地一个一个向外看旁边的token,在纸上写下:“变量是”
- 若向右遇到左圆括号,在纸上写下:“函数,参数是”,并用同样的方法处理括号中每一个参数——在纸上写下:“返回”
- 若向右遇到方括号,在纸上写下:“数组,长度为{方括号的内容},元素类型为”
- 若向右遇到右圆括号,什么也不做
- 若向左遇到*,在纸上写下:“指针,指向”
- 若向左遇到任何类型,在纸上写下对应的类型名
我们用这种方法来处理下面的声明
void*(*(*fp1)(int))[10]
- 从fp1开始——fp1是
- 向右,遇到右括号,什么也不做
- 向左,遇到*——指针,指向
- 向右,遇到左圆括号——函数,参数是int,返回
- 向左,遇到*——指针,指向
- 向右,遇到左方括号——数组,长度为10,元素类型为
- 向左,遇到*——指针,指向
- 向右,已经到声明结尾,什么也不做
- 向左,遇到void——void
结果是:fp1是 指针,指向 函数,参数是int,返回指针,指向数组,长度为10,元素类型为 指针,指向 void
这种方法对于人来讲是比较合适的,因为他比较符合人脑的处理方式,但是也有一点缺点,如果函数的形参也写了名字,不是很熟练的小白,就不容易找到正确的起始位置,造成处理的混乱。
对于机器处理,这种从中间到两边的方法就不是很合适了,因为机器并不能直接在一个token序列中直接找到处理的起始位置,他只能从左到右进行扫描,我昨晚灵机一动想到一个算法,今天进行了试验,效果良好,没有对比一些比如cdecl.org那样的开源实现,我这个算法只是一个demo,并不完整支持C声明的处理,地址在这里。
算法从左到右扫描,本质上是递归下降,基本过程是这样的,为了方便说明,递归函数名为parse:
- parse开始
- if遇到类型,保存进变量a,递归parse,输出a
- elif遇到*,递归parse,输出"pointer to"
- elif遇到左圆括号,递归parse,并检查括号匹配
- elif遇到标识符,输出"{标识符} is"
- 控制流继续
- if遇到左方括号,输出"array with length {长度} of",并检查括号匹配
- elif遇到左圆括号,输出"function accepting",循环地递归parse,吃掉后面的逗号,直到遇到右圆括号,输出"returning"
- 返回
递归的意义是什么?每一个parse函数的意义都是:“我处理的这段东西的类型是——”,破折号后面的东西右这一层函数退出后上一层函数来补完,所以回去看上面的算法,遇到一个类型的时候,我就明白我下面一层处理的这段东西的类型是int,所以我递归调用parse,并输出int。
再从循环不变式的角度看parse的递归,parse的出口只有一个,那就是遇到的第一个if,不管是什么样的函数声明,最后都是以一个变量结尾的,而这里也是唯一一个把话说全了的分支——其他的分支都是输出类似于“xxx是”,“xxx返回”这样的没说完的话的,所以parse保证上一层的话都没有说完——从不是第一个if的分支退出,由这一层把话补全,这算某种意义上的循环不变式吧。
最后我把上面的声明拆成不同层数来表现一下parse的过程
void
* [10]
( )
* (int)
( )
*
fp1
浅谈C语言变量声明的解析的更多相关文章
- 浅谈C语言中的强符号、弱符号、强引用和弱引用
摘自http://www.jb51.net/article/56924.htm 浅谈C语言中的强符号.弱符号.强引用和弱引用 投稿:hebedich 字体:[增加 减小] 类型:转载 时间:2014- ...
- 浅谈Java语言环境搭建-JDK8
title: 浅谈Java语言环境搭建-JDK8 blog: CSDN data: Java学习路线及视频 1.What's the JDK,JRE JDK(Java Development Kit ...
- C语言变量声明内存分配
转载: C语言变量声明内存分配 一个由c/C++编译的程序占用的内存分为以下几个部分 1.栈区(stack)— 程序运行时由编译器自动分配,存放函数的参数值,局部变量的值等.其操作方式类似于数据结 ...
- 浅谈C语言中断处理机制
一.中断机制 1.实现中断响应和中断返回 当CPU收到中断请求后,能根据具体情况决定是否响应中断,如果CPU没有更急.更重要的工作,则在执行完当前指令后响应这一中断请求.CPU中断响应过程如下:首先, ...
- 浅谈c语言结构体
对于很多非计算机专业来说,c语言课程基本上指针都不怎么讲,更别说后面的结构体了.这造成很多学生对结构体的不熟悉.这里我就浅谈一下我对结构体的认识. 结构体,就是我们自己定义出一种新的类型,定义好之后, ...
- C语言变量声明问题——变量定义一定要放在所有执行语句/语句块的最前面吗?
报错信息:error C2065: 'salary' : undeclared identifier #include <stdio.h> void main(){ printf(&quo ...
- 浅谈C# 匿名变量
每次写博客,第一句话都是这样的:程序员很苦逼,除了会写程序,还得会写博客!当然,希望将来的一天,某位老板看到此博客,给你的程序员职工加点薪资吧!因为程序员的世界除了苦逼就是沉默.我眼中的程序员大多都不 ...
- C语言变量声明加冒号的用法
有些信息在存储时,并不需要占用一个完整的字节, 而只需占几个或一个二进制位.例如在存放一个开关量时,只有0和1 两种状态, 用一位二进位即可.为了节省存储空间,并使处理简便,C语言又提供了一种数据结构 ...
- 浅谈JS的变量提升
JS的解析机制,是JS的又一大重点知识点,在面试题中更经常出现,今天就来唠唠他们的原理.首先呢,我们在我们伟大的浏览器中,有个叫做JS解析器的东西,它专门用来读取JS,执行JS.一般情况是存在作用域就 ...
随机推荐
- IOS中延迟执行的几种方法
前几天去国美在线面试,就遇到了上面的问题,当时是笔试,只写出来了第一种方法,现在整理了一下. //1.performSelector方法:在当前线程中执行的方法,使用默认模式,并延迟执行@select ...
- 转发 通过NAT和防火墙特性和TCP穿透的测评(翻译)
转自 http://blog.csdn.net/sjin_1314/article/details/18178329 原文:Characterization and Measurement of TC ...
- iOS后台挂起程序 当程序到后台后,继续完成Long-Running Task 任务
我们知道,到我们程序从前台退到后台(安home)键后,将执行程序的委托方法. // 当应用程序掉到后台时,执行该方法 - (void)applicationDidEnterBackground:(UI ...
- java虚拟机判断对象是否存活的方式
引用计数算法: 给对象添加一个引用计数器,每当有地方应用时,计数器值就加一,当引用失效时,程序计数器就减一,只要引用计数器的值为零时,就表示对象不可能再被引用,例如微软的 component ob ...
- JavaScript中字符串去掉特殊字符和转义字符
<pre name="code" class="javascript">/*** * 去掉字符串中的特殊字符 */ var excludeSpeci ...
- input常用属性
对于input的使用,大家都很熟悉的,<input> 标签用于搜集用户信息.根据不同的 type 属性值,输入字段拥有很多种形式.输入字段可以是文本字段.复选框.掩码后的文本控件.单选按钮 ...
- Jquery面试题
1.改变一个标签中的属性的值? 分为两种情况: 改变style中的css样式属性值: $("ID").css({color:red,font-size:30px....}); ...
- Express4.x常用API(二):Request
这是第二篇了,打算每天都拿些时间去详细的看看文档来自学下express吧,接下来就开始了,这次依然是有选择性的找API根据自己的理解翻译过来,方便日后开发时候的自己查阅,由于水平有限,大概也会在使用过 ...
- Kali Linux additional tools setup
The steps are pretty straight forward. The only tool that might cause some confusion is SMBexec. Thi ...
- js问题
1.原型链问题 1.js中万物皆对象,但对象也分为普通对象和函数对象,Object,Function都是js自带的函数对象,凡是通过 new Function() 创建的对象都是函数对象,其他的都是普 ...