`
javasalatu
  • 浏览: 723464 次
  • 性别: Icon_minigender_2
  • 来自: 北京
博客专栏
96df99eb-e89d-3228-9c8e-967fc745ec52
程序员的自我经营之道
浏览量:7701
文章分类
社区版块
存档分类
最新评论

C语言复杂声明的解析

 
阅读更多
C语言复杂声明的解析

(刘爱贵 Aiguille.LIU)

  C语言是一种通用的程序设计语言,它与UNIX系统之间具有非常密切的联系,C语言是在UNIX系统上开发的,并且,无论是UNIX系统本身还是运行其上的大部分程序,都是用C语言编写编写的。C语言很适合用来编写编译器和操作系统以及各种系统底层软件,因此被称为“系统编程语言”,但它同样适合于编写不同领域中的大多数程序。

  C语言中,指针的使用非常广泛。与其他方法相比较,使用指针通常可以生成更高效、更紧凑的代码。C语言常常因为声明的语法问题而受到人们的批评,特别是涉及到函数指针的语法。C语言的语法力图使声明和使用相一致。对于简单的情况,C语言的做法是很有效的,但是,如果情况比较复杂,则容易让人混淆,原因在于,C语言的声明不能从左至右阅读,而且使用了太多的圆括号。例如下面的指针声明:
char**argv
int(*daytab)[13]
int*dattab[13]
void*comp()
void(*comp)()
char(*(x())[])()
char(*(*x[3])())[5]
尽管实际中很少用到过于复杂的声明,但是,懂得如何理解和如何使用这些复杂的声明是很重要的,因为有时的确需要这样的声明,尤其对于C语言高级用户来说。另外,很多朋友在求职过程可能也会被问到这样的问题。

  C语言参考手册对声明语法作了详细的描述,简化的语法形式如下(dcl为declaration的缩写):
dcl: 前面带有可选的direct-dcl
direct
-dcl:name
(dcl)
direct
-dcl()
direct
-dcl[可选的长度]
  简而言之,声明符dcl就是前面可能带有多个*的direct-dcl。direct-dcl可以是name、由一对圆括号括起来的dcl、后面跟有一对圆括号的direct-dcl、后面跟有手方括号括起来的表示可选长度的direct-dcl。上面的语法要用来对C语言的声明进行分析。

  右左法则是常用的C语言复杂声明解析方法。右左法则其实并不是C标准里面的内容,它是从C标准的声明规定中归纳出来的方法。C标准的声明规则,是用来解决如何创建声明的,而右左法则是用来解决如何辩识一个声明的,两者可以说是相反的。右左法则的原文如下:

  The right-left rule: Start reading the declaration from the innermost parentheses, go right, and then go left. When you encounter parentheses, the direction should be reversed. Once everything in the parentheses has been parsed, jump out of it. Continue till the whole declaration has been parsed.


  其大致意思如下:

  右左法则:首先从最里面的圆括号看起,然后往右看,再往左看。每当遇到圆括号时,就应该掉转阅读方向。一旦解析完圆括号里面所有的东西,就跳出圆括号。重复这个过程直到整个声明解析完毕。

 《C程序设计语言》一书专门讲到复杂声明的解析,并且基于声明符的语法编写了解析程序。经修改和完善后的完整程序如下:
/**//*dcl.c*/
#include
<stdio.h>
#include
<string.h>
#include
<ctype.h>

#defineMAXTOKEN100

enum...{NAME,PARENS,BRACKETS};

voiddcl(void);
voiddirdcl(void);
intgettoken(void);
intgetline(chars[],intlim);
inttokentype;
chartoken[MAXTOKEN];
charname[MAXTOKEN];
chardatatype[MAXTOKEN];
charout[1000];

#defineBUFSIZE1024
charbuf[BUFSIZE];
charline[BUFSIZE];
intbufp=0;
intlinep=0;

intgetch(void)...{
if(bufp>0||(line[linep]!=''))
return(bufp>0)?buf[--bufp]:line[linep++];
else
returnEOF;
}

voidungetch(intc)
...{
if(bufp>=BUFSIZE)
printf(
"ungetch:toomanycharacters ");
else
buf[bufp
++]=c;
}


intmain(intargc,char*argv[])
...{
getline(line,BUFSIZE);
while(gettoken()!=EOF)...{
strcpy(datatype,token);
out[0]='';
dcl();
if(tokentype!=' ')
printf(
"syntaxerror ");
printf(
"%s:%s%s ",name,out,datatype);
}

return0;
}


intgettoken(void)
...{
intc;
char*p=token;

while((c=getch())==''||c==' ');
if(c=='(')...{
if((c=getch())==')')...{
strcpy(token,
"()");
returntokentype=PARENS;
}
else...{
ungetch(c);
returntokentype='(';
}

}
elseif(c=='[')...{
for(*p++=c;(*p++=getch())!=']';)
;
*p='';
returntokentype=BRACKETS;
}
elseif(isalpha(c))...{
for(*p++=c;isalnum(c=getch());)
*p++=c;
*p='';
ungetch(c);
returntokentype=NAME;
}
else
returntokentype=c;
}


intgetline(chars[],intlim)
...{
intc,i;

i
=0;
while(--lim>0&&(c=getchar())!=EOF&&c!=' ')
s[i
++]=c;
if(c==' ')
s[i
++]=c;
s[i]
='';
returni;
}


voiddcl(void)
...{
intns;

for(ns=0;gettoken()=='*';)
ns
++;
dirdcl();
while(ns-->0)
strcat(
out,"pointerto");
}


voiddirdcl(void)
...{
inttype;

if(tokentype=='(')...{
dcl();
if(tokentype!=')')
printf(
"error:missing) ");
}
elseif(tokentype==NAME)
strcpy(name,token);
else
printf(
"error:expectednameor(dcl) ");
while((type=gettoken())==PARENS||type==BRACKETS)
if(type==PARENS)
strcat(
out,"functionreturning");
else...{
strcat(
out,"array");
strcat(
out,token);
strcat(
out,"of");
}

}
程序dcl的核心是两个函数:dcl和dirdcl,它们根据声明符的语法对声明进行分析。因为语法是递归定义的,所以在识别一个声明的组成部分时,这两个函数是相互递归调用的。我们称该程序是一个递归下降语法的分析程序。 另外还有四个函数:
  getline():读入声明程序行,由用户从STDIN输入
  getch(): 模拟读入一个字符
  ungetch():模拟回入一个字符
  gettoken():跳过空格与制表符,以查找输入中的下一个token。

  使用dcl程序对上面复杂声明的例子进行解析可以得到如下结果:
char**argv
argv:pointertopointerto
char

int(*daytab)[13]
daytab:pointertoarray[
13]ofint

int*daytab[13]
daytab:array[
13]ofpointertoint

void*comp()
comp:functionreturningpointerto
void

void(*comp)()
comp:pointertofunctionreturning
void

char(*(*x())[])()
x:functionreturningpointertoarray[]ofpointertofunctionreturning
char
char(*(*x[3])())[5]
x: array[3] of pointer to function returning pointer to array[5] of
char
以上程序的编译和运行环境为:Redhat Linux 2.4 + gcc3.2.3。

参考文献:
1、C程序设计语言. Brian W. Kernighan & Dennis M. Ritche
2、c语言复杂声明的解析 http://hi.baidu.com/cuifenghui/blog/item/9b65e2cde2893d510fb345c4.html
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics