《C专家编程》随笔9:第三章C语言声明分析函数解析

C语言中,复杂的声明需要仔细分析。但一旦掌握了技巧,任何复杂的分析都显而易见。为了更清晰明白声明的分析过程,将详细分析声明的思路及函数。分析声明的过程如下:

下边是对第三章章尾的源码,及分析:

#include <stdio.h>

#include<string.h>

#include<ctype.h>

#include<stdlib.h>

#defineMAXTOKENS 100

#defineMAXTOKENLEN 64

 

enumtype_tag{

    IDENTIFIER,

    QUALIFIER,

    TYPE

};

 

structtoken{

    char type;

    char string[MAXTOKENLEN];

};

 

inttop = -1;

structtoken stack[MAXTOKENS];

structtoken this;

 

/****利用数组实现栈结构****/

#definepop stack[top--]

#definepush(s) stack[++top] = s

 

/*****将标记进行分类,将解释信息存入对应标记的解释字符串中,并返回标记的类型****/

enumtype_tag classify_string(void)

{

    char *s = this.string;

    if(!strcmp(s,"const"))

    {

        strcpy(s,"read-only");

        return QUALIFIER;

    }

    if(!strcmp(s,"volatile"))

        return QUALIFIER;

    if(!strcmp(s,"void"))

        return TYPE;

    if(!strcmp(s,"char"))

        return TYPE;

    if(!strcmp(s,"signed"))

        return TYPE;

   if(!strcmp(s,"unsigned"))

        return TYPE;

   if(!strcmp(s,"short"))

        return TYPE;

   if(!strcmp(s,"int"))

        return TYPE;

   if(!strcmp(s,"long"))

        return TYPE;

   if(!strcmp(s,"float"))

        return TYPE;

   if(!strcmp(s,"double"))

        return TYPE;

   if(!strcmp(s,"struct"))

        return TYPE;

   if(!strcmp(s,"union"))

        return TYPE;

   if(!strcmp(s,"enum"))

        return TYPE;

   return IDENTIFIER;

}

 

/*****

函数功能:从标准输入中取一个字符,并进行类型标记

 

取到空格舍弃;

若为字母或数字开头,则为标识符、限定符或类型,一直将紧随其后的字母或数字取出,假定标识符、限定符或类型全由字母和数字组成;

如果是*,则表示是一个指向...的指针;

若为其他,则将该字符作为其类型

*****/

voidgettoken(void)

{

    char *p =this.string;

    //从标准输入中取一个不为空的字符,放入this标记中

    while((*p = getchar()) == ' ');

    //判断是否是字母或数字,若是,则判断类型

    if(isalnum(*p))

    {

        while(isalnum(*++p = getchar()));

        ungetc(*p, stdin); //将最后一个非字母、非数字字符返回输入流

        *p = '\0';

        this.type = classify_string();

        return;

    }

    if(*p == '*')

    {

        //将指针的解释信息存入相应标记字符串

        strcpy(this.string, "pointerto");

        this.type = '*';

        return;

    }

    this.string[1] = '\0';

    this.type = *p;

    return;

}

 

/****

读取数据直到读出标识符;

打印出 标识符is

并从标准输入中继续读取下一个字符;

****/

voidread_to_first_identifier(void)

{

    gettoken();

    while(this.type != IDENTIFIER)

    {

        push(this);

        gettoken();

    }

    printf("%s is ",this.string);

    gettoken();

}

 

/******

由于是数组,则首先打印 array

然后从标准输入中取一个字符,由于在gettoken中会将连续数字取出,

并标记为标识符(这个标识符类型在这里无所谓,由于下边没判断,直接判断是不是字),

如果字符串是字,则将字符串变为int型数值,减一是因为数组的最大存储下标比数组的大小小1.

在操作完之后,会读取]之后的下一个标记,进行后续函数处理,并打印出 of

******/

voiddeal_with_arrays(void)

{

    while(this.type == '[')

    {

        printf("array ");

        gettoken();

        if(isdigit(this.string[0]))

        {

            printf("0..%d",atoi(this.string)-1);

            gettoken();

        }

        gettoken();

        printf("of ");

    }

}

 

/******

如果是函数,则将参数忽略,直到读出)

再读出)后的下一个标记,进行后续函数处理,

打印出function returning

******/

voiddeal_with_function_args(void)

{

    while(this.type != ')')

    {

        gettoken();

    }

    gettoken();

    printf("function returning ");

}

 

/****

判断前一个标记是否为*

若为,则打印出pointer to

****/

voiddeal_with_pointers(void)

{

    while(stack[top].type == '*')

    {

        printf("%s ",pop.string);

    }

}

 

/****

该函数紧跟read_to_first_identifier,由于在read_to_first_identifier读取到标识符后又取了一个字符,

判断紧随标识符其后的字符的类型,进行处理;

若为 [,则说明为数组,调用deal_with_arrays

若为 (,则说明为函数,调用deal_with_function_args;这两个函数都会在操作后再取一个标记;

然后判断前一个标记是否为指针;

然后判断前一个标记是否为(,若是,则忽略之前从标准输入中取得标记,认为为)

将这部分作为整体,再次取一个标记,再次进行处理判断;

若不是,则打印出弹出的标记注释

*****/

voiddeal_with_declarator(void)

{

    switch(this.type)

    {

        case '[': deal_with_arrays();break;

        case '(':deal_with_function_args();break;

    }

    deal_with_pointers();

    while(top >= 0)

    {

        if(stack[top].type == '(')

        {

            pop;

            gettoken();

            deal_with_declarator();

        }

        else

        {

            printf("%s ",pop.string);

        }

    }

}

 

intmain(void)

{

    read_to_first_identifier();

    deal_with_declarator();

    printf("\n");

    return 0;

}

 

 

该源码没有进行错误判断,仅对正确的声明处理,且在涉及多个括号的包含时,如果不从最里层开始处理,可能出现错误,如:

由于对’(‘匹配的是右边第一个’)’,所以当匹配不对时,如前三个例子,总是解析错误。而对于最后一个,由于这个例子正好使得匹配括号正确,所以解析正确。

故对源代码进行修改,对’(‘进行计数,使得都能匹配正确的’)’。可以看以下结果。

 

 

其中在解决上述问题时,修改的代码部分如下:

/******

如果是函数,则将参数忽略,直到读出)

再读出)后的下一个标记,进行后续函数处理,

打印出 function returning

******/

void deal_with_function_args(void)

{

 

    /****代码更改开始****/

   int symbol_num = 1; //记录'('的数量,由于进入这个函数本身就需要匹配一个'('

   gettoken();

    while( symbol_num != 0)

    {

       if( this.type == ')' )

           symbol_num--;

       if( this.type == '(' )

           symbol_num++;

       gettoken();

    }

     /****代码更改结束****/

 

 

   /***原代码****/

   /****

   while(this.type != ')')

    {

       gettoken();

    }

   gettoken();

    ****/

 

 

   printf("function returning ");

}

 

 

 

int main(void)

{

   while(1)

    {

    read_to_first_identifier();

    deal_with_declarator();

    printf("\r\n***************\r\n");

    fflush(stdin);

    }

 

   return 0;

}

 

修改后的代码,对正确的声明可以解析(如果还有bug,可以评论区交流)。但该代码未对错误格式的声明的解析进行错误处理,解析的结果也根据输入的错误声名有关。

总过此程序,将深入理解C语言的声明。

 

在学习这个代码的过程中,发现了自己学C语言的一个错误,至于怎么错就不误扰大家了,下边说明以下两个声明的解释方式。

int * const *  point_of_point

point_point 是一个指针,该指针指向一个只读指针,这个只读指针指向int型数据。

 

int * * const  point_of_point

point_point 是一个只读指针,该只读指针指向一个指针,这个指针指向int型数据。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值