dcl程序是基于对声明符的语法进行详细描述。
反思:out[0] = ‘ ’;的重要性dcl : 前面带有可选的*的 direct-dcl
direct-dcl: name
(dcl)
direct-dcl()
direct-dcl[可选的长度]
虽然在第一次将声明转换为文字描述时,这行语句没有意义。因为静态数组在没有认为定义的情况下,会自动将其各元素设置为’ ’
但是,当我们进行第二次转换时,第一次的声明还储存在out数组中,这不是我们所需要的。因为strcat函数只会找到该数组中的终止符位置,将字符串常量scr追加到目标字符串dest,将目标字符的终止符覆盖掉,然后在添加一个终止符位。
以下为相关结果显示:
没有out[0] = ' ’语句
添加out[0] = ' ’语句后:
#define _CRT_SECURE_NO_WARNINGS #include#include #include #include #define MAXTOKEN 100 enum{NAME, PARENS, BRACKETS}; //NAME是变量名的标志 PARENS是圆括号的标志 BRACKETS是方括号的标志 void dcl(void); void dirdcl(void); int gettoken(void); int tokentype; //最后一个记号的类型 char token[MAXTOKEN]; //最后一个记号字符串(后读取的记号字符串会覆盖掉之前读取的) char name[MAXTOKEN]; //标识符名 char datatype[MAXTOKEN];//数据类型为char,int等 char out[100]; //输出串 //将声明转化为文字描述 int main() { while (gettoken() != EOF) { //该行的第一个记号是数据类型,输入的正确声明中 strcpy(datatype, token); //tokentype=NAME时,刚开始就是数据类型,接下来就是变量名(name) out[0] = ' '; dcl(); //分析该行的其余部分,主函数的调用暂时中止 if (tokentype != 'n') printf("syntax errorn"); printf("%s: %s %sn", name, out, datatype); //由此我们发现声明的文字叙述可以分为3部分:变量名,待分析部分,数据类型 //在该例子中将这三个部分分开放入各自数组中进行处理 } //变量名和数据类型部分的位置是固定的,分别在最前面和最后面 system("pause"); return 0; } //dcl(declaration):对一个声明符进行语法分析 void dcl(void) { int ns; //在判断是否为直接声明时,先看一下其前面是否有解引用运算符(通过指针间接访问) for (ns = 0; gettoken() == '*';) //统计字符'*'的个数 ns++; dirdcl(); while (ns-- > 0) //跳出dirdcl函数,就表明对于全部或者圆括号内(部分)的直接声明来说 strcat(out, " pointer to"); //我们已经将其分析好了,那么接下来就可一个先将“指向(直接声明)”的字样存储进来 } //分析直接声明,通过gettoken函数返回代表记号类型的值,来进行相应的语法分析 void dirdcl(void)//根据前面提供的语法可知,直接声明简单来说分为 //以“字母”开头的变量名 //通过圆括号,嵌套一个声明dcl(以字符“(”开头) //变量名+“()”or"[]"(以“字母”开头的变量名) { //所以当直接声明不以"("或者“变量名”开头,那么输入内容就是有问题的 int type; if (tokentype == '(') { //如果有左括号就需要再次判断圆括号中是否有'*'运算符 dcl(); //在分析完圆括号中的内容后,返回到之前被递归调用打断的部分 if (tokentype != ')')//如果此时接下来的记号不是右括号,那么需要报错 printf("error:missing )n"); } else if (tokentype == NAME) //若第二次返回该记号类型,表明其为变量名,则将其放入name数组中 strcpy(name, token); else printf("error: expected name or (dcl)n"); while ((type = gettoken()) == PARENS || type == BRACKETS) if (type == PARENS) strcat(out, "function returning"); else { //当返回的记号类型为方括号时,表明对于声明的文字描述中要有“array[size] of”字样的出现 strcat(out, " array"); strcat(out, token); strcat(out, " of"); } } int gettoken(void) //返回下一个标记 { int c, getch(void); void ungetch(int); char *p = token; while ((c = getch()) == ' ' || c == 't') ; if (c == '(') { if ((c = getch()) == ')') { strcpy(token, "()"); return tokentype = PARENS; } else { //如果左括号和有括号之间有内容,则需要先将多入的字符保存起来,对其里面的内容进行分析 ungetch(c); return tokentype = '('; } } else if(c=='['){ for (*p++ = c; (*p++ = getch()) != ']';) //在遇到左方括号时,一直读取内容直到右方括号的出现 ; *p = ' '; return tokentype = BRACKETS; } else if (isalpha(c)) { //判断字符c是否为大写字母或小写字母 for (*p++ = c; isalnum(c = getch());) //在将字符放入token数组后,再次判断下一个字符 *p++ = c; //是否为字母或是数字 *p = ' '; ungetch(c); //为了保证读取内容的完整性,往往要多读取一个字符。为了保证不遗漏掉多读取的内容 //需要将其临时存储在一段空间中 return tokentype = NAME; } else return tokentype = c; } #define BufSize 100 int Buf[BufSize]; int *bufp = Buf;//下一个空闲位置 int getch(void) { return bufp > Buf ? *--bufp : getchar(); } void ungetch(int c) { if (bufp < Buf + BufSize) *bufp++ = c; else printf("buffer is full"); }



