您的位置:首页 > 编程语言 > C语言/C++

程序实现分析C语言的声明语句含义

2012-11-30 16:41 260 查看
本程序参考了《C专家编程》一书中第3章“分析C语言的声明”中图3-1(65页)以及71页、72页的伪代码实现。程序能完成基本分析功能。如有问题,欢迎给我留言讨论。

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#define MAXTOKENLEN 20
#define STACKLEN 20
#define STRINGSIZE 20

#define QUALIFIER 'q'
#define IDENTIFIER 'i'
#define TYPE 't'

/**
**函数实现:通过程序分析C语言中的声明,尤其是函数、数组、指针等混在一起的复杂形式
**比如:int ( * fun ( ) ) ( ) ; 或者 char * const * ( * next ) ( ) ; 等
*/

/**

**其中,读入的符号分为三种类型:类型符,如int等;限定符,如*,const,volatile等;标识符,就是名字字符串。

**第一步:我们自左向右读取,将所有标记压入堆栈,直到标识符为止。

**第二步:读入标识符右边的符号。如果是方括号,将[可能的大小]读完,输出“……的数组”;

**第三步:如果是左括号,将(可能的参数)读完,即直到右括号为止,输出“返回……的函数”。

**第四步:如果左边的符号(堆栈中的那个符号)是一个左括号,一直读,直到遇见对应的右括号,然后跳到第二步;

**第五步:如果左边的符号是const,volatile,*之一:如果是const,输出”只读“;如果是volatile,输出”volatile“;
**如果是*,输出”指向……的指针“。继续向左边读(即出栈),直到所读符号不再是三者之一,然后重复第四步。

**第六步:将堆栈中剩下的符号形成声明的基本类型。
*/

struct token
{
char type; //符号的类型
char string[STRINGSIZE]; //符号名字
};

struct token stack[STACKLEN]; //符号堆栈
struct token present; //用来保存刚刚读入那个符号
int top = -1; //指向栈顶元素

/**对读入的token进行分类,包括标识符identifier、类型type和限定符qualifier*/
char token_classify(char *string)
{
if(strcmp(string,"const") == 0 || strcmp(string,"volatile") == 0 || strcmp(string,"*") == 0) //如果是限定符
return QUALIFIER;
else if(strcmp(string,"int") == 0 || strcmp(string,"long") == 0 || strcmp(string,"short") == 0 ||
strcmp(string,"double") == 0 || strcmp(string,"unsigned") == 0 || strcmp(string,"float") == 0 ||
strcmp(string,"char") == 0 || strcmp(string,"enum") == 0 || strcmp(string,"union") == 0 ||
strcmp(string,"struct") == 0 || strcmp(string,"void") == 0) //如果是类型符
return TYPE;
else if(strcmp(string,"(") == 0 || strcmp(string,")") == 0) //对于'('和')'单独处理
return ' ';
else //排除其他情况后,剩余标识符
return IDENTIFIER;
}

/**读入下一个字符或字符串,并调用分类函数进行分类处理*/
void get_token()
{
char *p = present.string; ////读入后暂存在present当前符号变量中,指针指向present.string
while((*p = getchar()) == ' '); //跳过空格
if(isalnum(*p)) //如果是字母或数字
{
while(isalnum(*p))
{
*++p = getchar();
}
ungetc(*p,stdin); //退回非字母或数字的那个符号,用于下次读入
}
else
{
p++; //如果是其他符号,比如'('')''*'直接读入
}
*p = '\0'; //加上字符串结束标志
present.type = token_classify(present.string); //分类
}

/**读至标识符,从左至用入栈,并显示标识符内容,第一步*/
void read_to_first_identifier()
{
get_token();
while(present.type != IDENTIFIER) //如果没读到标识符,将之前左边的符号全部入栈
{
top++;
stack[top] = present;
get_token();
}
printf("标识符是%s,",present.string); //输出标识符名字
get_token();
}

/**处理数组,第二步*/
void deal_with_arrays()
{
while(present.string[0] != ']') //如果还没读到匹配的']',继续读,这样就将数组大小读入并忽略了
get_token();
printf("数组,该数组类型是"); //读到后输出它是一个数组
get_token();
}

/**处理函数,第三步*/
void deal_with_functions()
{
while(strcmp(present.string,")") != 0) //如果还没读到匹配的')',继续读,这样就将函数的参数都读入并忽略了
get_token();
printf("函数,该函数返回"); //读到后输出它是一个函数
get_token();
}

/**处理限定符,第五步*/
void deal_with_qualifiers(char *string) //限定符有三类,根据名字分别处理
{
if(strcmp(string,"const") == 0) //输出只读
printf("只读");
else if(strcmp(string,"*") == 0) //输出它是一个指针
printf("指针,这个指针指向");
else
printf("volatile"); //输出volatile
}

/**处理声明器,第二步,第三步,第四步,第五步,第六步*/
void deal_with_all()
{
struct token temp;
//第2步
step2:
if(strcmp(present.string,"[") == 0) //标识符右边,如果是'[',作为数组处理
deal_with_arrays();
//第3步
else if(strcmp(present.string,"(") == 0) //标识符右边,如果是'(',作为函数处理
deal_with_functions();
//第4步
step4:
temp = stack[top--]; //取栈顶元素,即标识符左边符号
if(strcmp(temp.string,"(") == 0) //如果是'(',读到标识符右边')'为止
{
while(strcmp(present.string,")") != 0)
get_token();
get_token();
goto step2; //跳到第2步
}
//第5步
else if(temp.type == QUALIFIER) //如果标识符左边符号是三个限定符之一
{
deal_with_qualifiers(temp.string); //输出限定符代表的输出
while(stack[top].type == QUALIFIER) //获得堆栈中下一元素,如果仍为限定符继续输出知道不是限定符为止
{
deal_with_qualifiers(stack[top].string);
top--;
}
goto step4; //跳到第4步
}
//第6步
else if(temp.type == TYPE) //将剩下的符号作为声明的基本类型输出
{
printf("%s",temp.string);
}
}

int main()
{
read_to_first_identifier();
deal_with_all();
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: