岁月流转,往昔空明

C++博客 首页 新随笔 联系 聚合 管理
  118 Posts :: 3 Stories :: 413 Comments :: 0 Trackbacks
在进行静态强类型语言的设计过程中,是一定需要提供一种语法实现变量和类型的关联。这种语法一般称之为 “声明” 或者 “定义”。

如果用一种人类易读的方式进行表达,可以写作 define variable_name as type_name。没有错,你看到的这种形式类似于VB当中 Dim As的方法。在语法分析的过程中,这种方法的好处很明显。define 作为提示字,意味着一个声明或者定义的开始,as 则代表后续的Tokens都是用于一个类型的,这样关键字可以很显著的将语义区分开。

但是这种写法并不方便。C 一类的语言,都选用一种 prefix declaration specifier 的方式,也就是我们常常见到的:type_name variable_name 这样的形式。举个最简单的例子,int i;。如果有类型修饰,可以作为一个基本类型的前缀或者后缀出现。例如const int i; 或者 int const i; 至于修饰含义是左绑定还是右绑定,这个需要取决于语言本身的设计。

如果事情是这个样子的话,也就没什么好说的了。但是在C语言里面,有两个例外:函数和数组。例如,在C里面定义一个数组,写作:const int array[expr]; 这种写法既不是前缀写法,也不是后缀写法。类型被变量两分了。如果仅仅是一个数组定义,那也好办,当做特殊情况处理就好了。
但是有时候我们的数组元素会变得非常复杂。 举个例子,( struct {...} const [ constant ] identifier ( params... )  ) [ expr ]。你能理解这样一个复杂的定义其实是一个函数的数组么?不仅仅是你不能,我想在撰写语法规则的时候,又困难,又不合逻辑。

在C的EBNF中,这个问题解决起来也很复杂。
在这里,我们简化一下C的语法,不考虑C的指针,不考虑变量初始化,不考虑类型修饰符,也不考虑一个类型定义多个变量的情况。
这就意味着你只能写 int i; int j; j = 0; 而不能写 int i, j = 0;

declaration ::= declaration_specifier declarator

declarator ::= identifier
               | delcarator '['  expr ']'
               | declarator '(' parameters_declaration_list ')'
               | '(' declarator ')'


那么我问你,declarator是个什么东西。变量名?不是。函数声明?也不是。数组?也不是。declaration_specifier呢?变量类型?是。数组元素类型?是。函数返回值类型?也是。显然这样一个语法要素具备了太多的语义。更重要的是,没到最后,你是没法确定declaration_specifier究竟是一个什么含义,identifier所代表的,究竟是个什么东西。显然只有在声明匹配完成之后,还需要进行复杂的推导过程,才能确定变量的嵌套结构。

这个问题还导致了C语言里面的这么一个特性:那就是很出名的指针符号*的变量绑定性质。在C语言中,*,[],() 操作符并没有理解为对类型的修饰,而是理解为对变量的修饰。这就让我们必须要这么写: int m[expr], n[expr]; int m( int, int ), n( int, float );
而对变量,写法就成了: int x, y; 这导致了同样的写法两者在语义上的不一致性。这也是为什么新手云里雾里的根本原因了。按照普通变量的规矩,函数应该写成 int (x, y) (int);这样的结构。好吧,这样还挑战不倒你。但是如果我更复杂一点,加上初始化呢?就变成了 int ( x = p0, y ) (int) 这样的结构,呃。

为什么C会这么做?难道完全的前置类型会让语法分析工作变难么?很显然不会。虽然C语言如此声明的出发点不可考,但是想让普通类型和数组声明维持同样的语法结构是很简单的事情,C#就给了一个很好的答案。在C#中,变量是如此声明的:

declaration ::= declaration_specifier declarator
declaration_specifier ::= function_specifier | array_specifier | identifier
                          | '(' declaration_specifier ')'
function_specifier ::= declaration_specifier '(' parameter_declaration_list ')'
array_specifier ::= declaration_specifier '[' expression_list ']'


这样,declaration_specifier完全就变成了类型,而declarator部分就和类型脱钩了。在这种声明方式中,我们就这么定义一个变量 int [] x, y, z; OK,这样大家就理解了,x,y,z都是一个数组。而不会造成C语言当中的误解。

最后讨论一下类型修饰的问题。这里只讨论单一类型的类型修饰,下面我们会看到,复合类型其实也是一样的。我们将类型声明和表达式进行类比,就可以将类型理解为变量,类型修饰理解为单目操作符。

const type  <==类比==>  ~var

const就相当于按位取反操作符~,type就相当于var。const type这个表达式的结果就是一个常量化的type。那么,我们可以更广泛的将声明理解成 type_expression variable_name 这样的形式。type_expression在语义分析的时候进行类型演算得出结果,variable_name则利用类型表达式获得真正的类型,并实例化。下面我们写出type_expression的演算语法:

type_expression ::= type_identifier
                    | type_op type_expression  /* prefix  style */
                    | type_expression type_op  /* postfix style */
type_op ::= type_qualifier

当然,对于 const int volatile 这样的声明,这个表达式还有二义性。这个二义性可以通过一定的方法消除,这一点一般的编译原理教材都有详细的讨论。并且,我们完全可以将()和[]也纳入到type_operator中,这两个操作一个可以构造出函数类型,一个构造出数组类型来,这样类型问题就得到了一个递归一致的解决。

当然,更重要的是,我将type_expression variable_name这样的声明式变成typedef type_expression variable_name呢?

哈哈。

最后严重感谢一下VCZH,因为这小子吃了足够的shit,我就可以不用继续吃shit了。正所谓前人栽树后人乘凉。

posted on 2009-02-25 01:33 空明流转 阅读(1961) 评论(2)  编辑 收藏 引用

评论

# re: 静态强类型语言的类型声明与变量声明 2009-02-25 01:43 陈梓瀚(vczh)
囧  回复  更多评论
  

# re: 静态强类型语言的类型声明与变量声明[未登录] 2009-02-27 09:37 六水
哈哈,这句说的太经典了  回复  更多评论
  


只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理