0.引子
int* arr[10];
arr是一个数组,其中的元素是int*类型的。
int (*ptr)[10];
ptr是一个指针,指向的元素是具有10个int元素的的数组。
1+2*3
大家知道先乘除后加减,如果要先算1+2的话,就要写成
(1+2)*3
1.结合性
文法 (1):
A := A+i | i
对应的语言具有i+i+i的形式。
其规约也毫无压力:
i+i+i->A+i+i->A+i->A.
再看个类似的文法(2):
A := i+A | i
对应的语言和上面的相同,规约:
i+i+i->i+i+A->i+A->A
再看个文法(3):
A := A+A | i
对应的语言没有变,但是存在如下规约:
i+i+i->A+i+i->A+A+i->A+i->A+A->A
i+i+i->i+i+A->i+A+A->i+A->A+A->A
于是有歧义。
如果给一个i+i+i这样的句子,是有歧义的,于是规定+具有左结合性,
于是就可以消歧了,换句话说,指定了文法(1)。
由于加法具有结合律,所以,歧义并不影响结果,但是,如果是减法或者除法
就会jiong掉了。
2.优先级
文法(4):
A := A+B | B
B := B*i | i
给定句子i+i*i,可以有如下规约:
i+i*i->i+B*i->i+B->B+B->A+B->A
这样的规约就是“先乘除后加减”的直接体现。
再看下面的:
i+i*i->B+i*i->A+i*i->A+B*i->A*i->A*B ???
规约不下去了。
如果给定一个i+i*i这样一个句子,也是有歧义的,于是可以规定先乘后加,
于是就可以消歧了,换句话说,指定了文法(4)。
到这里就可以明白了,在这里所谓结合性和优先级,就是指定了某个文法。
如果文法改成:
A := A+B | B
B := B*C | C
C := i | (A)
于是就可以接受(i+i)*i之类的句子了,其中用意,你懂的。
3.
文法(5)
D := PD
PD := *PD | DD
DD := i | DD[number]
number是一个非负整数。
首先看看能被文法(5)接受的句子:
i
*i
i[10]
*i[10]
*****i[10][10][10]
如果在前面加个int,在最后加一个;:
int i;
int *i;
int i[10];
int *i[10];
int *****i[10][10][10];
这就是声明啊。
话说*i[10]才是我们关心的,尝试对这个进行规约:
*i[10]->*DD[number]->*DD->*PD->PD->D
如果试图先对前面的进行规约:
*i[10]->*DD[10]->*PD[10]->PD[10]->D[10]???
又走不动了。
所以,给定了文法,在这里没有歧义, i总是和后面的[10]形成一个语法单位,再和前面的*
形成一个语法单位。
如果要求先和前面的*形成语法单位呢?
D := PD
PD := *PD | DD
DD := i | DD[number] | (D)
number是一个非负整数。
于是对 (*i)[10]有如下规约:
(*i)[10]->(*DD)[10]->(*PD)[10]->(PD)[10]->(D)[10]->DD[10]->DD[number]->
DD->PD->D.
到了这里,相信大家懂的。
4.
当有人对int *arr[10];和int (*ptr)[10];的区别进行解释,并试图用优先级和结合律进行解释的时候,
大家懂的。
5.
进一步说明:
在第1,2节中,最后说“如果给定一个i+i*i这样一个句子”,这里是指没有文法的情况下,因为这样的句子
首先在数学上有,然后在指定结合性和优先级的时候就等同于指定文法。
而在第3节末尾,就没有类似的说法了,而是指定了文法,再说没有文法歧义。
PS:
木有检查,如果有bug或者叙述不严格的地方,敬请指教。
2011/01/19 17:22
创建
2011/01/19 17:29
增加“进一步说明”
2011/02/16 13:28
文中以前用推导的地方应该用规约,其逆过程才叫推导。
2013/06/29
瞄了一下龙书,发现这些东西在龙书中已经说清楚了,进一步理解需要参考:
龙书,第二版,中文,29到30页。
Alfred V.Aho,Monica S.Lam,Ravi Sethi,Jeffrey D.Ullman著,赵建华,郑滔,载新宇译,编译原理,机械工业出版社,p29-30
读者可以参考声明转换以加深对C++声明的理解