在
C
语言里,指针是最灵活的数据类型,它具有低级语言的特点,高效快速,不过学会它就不是那么容易了。由于指针是直接面向机器的,也就是它是指向内存的地址,因此使用
C
来编写嵌入式软件,或者操作系统的软件是比较合适的选择。下面就来看例子里的指针语句,如下:
typedef char * va_list;
上面这句声明了
va_list
为
char
的指针类型的别名,那么在
LCC
里又是怎么样处理它的呢?
先识别
typedef
出来,接着就调函数
decl(dclglobal)
,然后调用函数
specifier
来处理。在函数
specifier
里:
#001 //
说明
#002 static Type specifier(int *sclass)
#003 {
#004 int cls, cons, sign, size, type, vol;
#005 Type ty = NULL;
#006
#007 cls = vol = cons = sign = size = type = 0;
…
#027 case STATIC:
#028 case EXTERN:
#029 case TYPEDEF:
#030 p = &cls;
#031 t = gettok();
#032 break;
#033
…
在第
29
行到第
32
行处理
typedef
记号,接着获取下一个记号,就是
char
,它也是在
specifier
函数里处理,如下:
#061 case VOID:
#062 case CHAR:
#063 case INT:
#064 case FLOAT:
#065 case DOUBLE:
#066 p = &type;
#067 ty = tsym->type;
#068 t = gettok();
#069 break;
由于
char
是关键字,所以在初始化类型时,已经建立起来,在词法分析里就可以找到它的符号信息
tsym
,因此在第
67
行就可以获取它的类型了。
接着下来再取下一个记号,它是
’*’
,在函数
specifier
里是没有处理的,直接就跳到下面的语句运行了:
#107 default:
#108 p = NULL;
在第
108
行里设置
p
为空,就从跳出了
for
循环,接着就从
specifier
里返回了
CHAR
的类型。
从那里返回后,就调用函数
dclr
处理,在那里再调用函数
dclr1
来处理。它的处理代码如下:
#001 static Type dclr1(char **id, Symbol **params, int abstract)
#002 {
#003 Type ty = NULL;
#004
…
#019 case '*':
#020 t = gettok();
#021 if (t == CONST || t == VOLATILE)
#022 {
#023 Type ty1;
#024 ty1 = ty = tnode(t, NULL);
#025
#026 while ((t = gettok()) == CONST || t == VOLATILE)
#027 ty1 = tnode(t, ty1);
#028
#029 ty->type = dclr1(id, params, abstract);
#030 ty = ty1;
#031 }
#032 else
#033 ty = dclr1(id, params, abstract);
#034
#035 ty = tnode(POINTER, ty);
#036 break;
在第
19
行里,就是处理指针的类型。
第
20
行获取下一个记号,在这句语句里就是
ID
(
va_list
)。因此在运行第
33
行的代码,再仔细地看一下第
33
行代码,它居然还是递归调用本函数
dclr1
。这就是递归下降的语法分析。由于指针又可以指向指针,这样的语法一定需要递归地分析的。在第二运行
dclr1
函数里,就运行到
ID
的处理代码:
#007 case ID:
#008 if (id)
#009 {
#010 *id = token;
#011 }
#012 else
#013 {
#014 error("extraneous identifier `%s'/n", token);
#015 }
#016
#017 t = gettok();
#018 break;
在第
10
行里就可以返回
ID
(
va_list
),并返回空类型给第
33
行的
ty
。最后就是调用函数
tnode
来构造一个指针类型,这样就保存
va_list
到符号表里,并且声明它是
char
的指针类型。
在
decl
函数需要把前面分析出来的基本类型放到这个指针的类型里,它的处理代码如下:
#004 Type ty = dclr1(id, params, abstract);
#005
#006 for ( ; ty; ty = ty->type)
#007 {
#008
#009 switch (ty->op)
#010 {
#011 case POINTER:
#012 basety = ptr(basety);
#013 break;
#014 case FUNCTION:
#015 basety = func(basety, ty->u.f.proto,
#016 ty->u.f.oldstyle);
#017 break;
#018 case ARRAY:
#019 basety = array(basety, ty->size, 0);
#020 break;
#021 case CONST: case VOLATILE:
#022 basety = qual(ty->op, basety);
#023 break;
#024 default: assert(0);
#025 }
#026
#027 }
#028
#029 if (Aflag >= 2 && basety->size > 32767)
#030 warning("more than 32767 bytes in `%t'/n", basety);
#031
#032 return basety;
第
4
行里返回指针的类型。
在第
6
行里就用
for
循环把所有类型添加到一个链表
basety
里。
在第
11
行到第
13
行是调用
ptr
来添加类型到链表。
在第
32
行就可以返回这个指针链表的类型,这样就可以给后面判断语法和语义了。
通过上面的代码就实现了指针类型的语法分析。