小杨同志

导航

 

 

       之前对C语言两个核心的方面做了不少的阐述,但是总觉得缺少点什么,想补充又感觉都是赘述,前天老师留了个作业,完成一个计算器的程序,里面用到了栈的数据结构,而这个栈很特殊,终于明白啦,缺少的就是一个具有说服力的实践程序来展现着一点。

计算器大家都知道,也就是输入一个表达式,例如:2.4*(4.5+4)+6+(-8.0)/5+6^6,输入时都是字符串,问题的关键是区分优先级,字符和数字。这些都不是今天我要说的重点,但一会儿会提供计算器程序的源代码供大家参考,也就是对今天所涉及的知识进行测试。

下面进入正题,要想完成这个程序就要用到栈,几个?两个,一个存放操作符,一个存放待操作的数据(float),但是一般情况下,结点的数据类型是自己定义的,并且是只能有一种类型的,也就是只能存放一种类型的数据,不可能把float和char作为不同的结点压入一个栈内。有的人说,我们可以编写两种栈,一种是用来存放float的,一种是用来存放char,只是做很少的改动就行啦。我同学几乎都是这么做的,这样做虽然程序的效率不会较少,但是程序所占用的空间是增大了一倍,再问一句,假如程序需要4种栈来存放4种数据类型,怎么办,做四个?运行时太浪费内存,显然这也违背了可重用代码的原则。我的意思大家肯定猜到啦,就是编写一种栈,但是可以存放任何数据类型。

我们就拿float型的操作数和char型的操作符说事。基本思想:利用指针,将指针所指向的数据的内容复制到栈空间指定的位置,这里有个关键的“中间人”,void * pdata,因为void *可以指向任何数据类型,也就是作为中转站的角色。复制几个?由你指定。假如我们要将float数据ftemp入栈,只需调用memcpy()函数(类库所提供的,可以直接调用),复制4个字节即可,该函数有三个参数:目标地址,源地址,要复制的字节数。假如要将char入栈呢?就是把目标地址和它的地址传入这个函数作为前两个参数,第三个参数是1。大家不要误解,这里是利用同一种栈,这种栈可以兼容很多数据类型,定义了两个栈变量,一个专门存放float,一个专门存放char。我们还是看程序和注释吧,这样更直观。

 

#include "stdlib.h"

 

typedef struct stack{

       char * buffer;              /*定义了一个指向char的指针*/

       int max;                                    /*记录栈最大空间*/

       int top;                                      /*始终指向栈顶元素*/

}Stack;

 

Stack * CreateStack(int max,int length)              /*max是数据的个数,length是每个数据的长度*/

{

       Stack * ps;

      

       ps = (Stack *)malloc(sizeof(Stack));

       if(!ps)

              return 0;

       ps ->buffer = (char *)malloc(length * max);/*开辟了max*length,这么大的空间,需要转化成char *类型*/

       if(!ps ->buffer)

       {

              free(ps);

              return 0;

       }

       ps ->top = -1;                                         /*空栈初始值*/

       ps ->max = max * length;       

       return ps;

}

int IsEmpty(Stack * ps)

{

       return ps ->top == -1;

}

int IsFull(Stack * ps,int length)

{

       return (ps ->max - ps ->top - length) / length == 0;/*不做解释,自己理解*/

}

void ClearStack(Stack * ps)

{

       ps ->top = -1;

}

void DestroyStack(Stack * ps)

{

       ClearStack(ps);

       free(ps ->buffer);

       free(ps);

}

 

int Push(Stack * ps,void * pdata,int length)        /*void * pdata,接收要压栈数据的地址,可以接受任何指针类型,这是这个栈实现的最关键的地方,无论是float,char,还是结构体类型*/

{

       if(IsFull(ps,length))

              return 0;

       ps ->top += length;                         /*先指向下一个元素的位置*/

      memcpy(&ps ->buffer[ps ->top],pdata,length);      /*利用memcpy函数进行数据的拷贝,如果是float,则length是4*/

       return 1;

}

int Pop(Stack * ps,void * pdata,int length)     /*出栈时,pdata指向的是接收出栈数据的变量的地址,不管是什么类型,我只在乎将合适长度的数据拷贝到合适的位置,然后拿来就用即可*/

{

       if(IsEmpty(ps))

              return 0;

     memcpy(pdata,&ps ->buffer[ps ->top],length);/*将栈内的数据拷贝到了合适的位置*/

       ps ->top -= length;

       return 1;

}

int GetTop(Stack * ps,void * pdata,int length)

{

       if(IsEmpty(ps))

              return 0;

       memcpy(pdata,&ps ->buffer[ps ->top],length);

       return 1;

}

 

       既然栈可以实现这样的“数据的泛化”,那么链表,队列,树等等,可以吗?答案是肯定的。

紧着着,你就可以写写这些数据结构,封装起来,制作成.lib文件,你拥有了自己的库函数,以后拿来就用!

 

       就这些啦,然后我们看一看这个计算器程序,注:由于时间原因,我只是实现了基本的功能,sin,cos,开方,还没有写,但是为了便于扩展,将这几个操作函数作为函数指针数组呈现出来,扩展时只是将扩展的函数名假如数组即可。下面是程序,看看如何使用刚才的栈的。

       计算器的基本算法:扫描整个表达式,碰见数字则入栈,碰见操作符,则与栈顶的操作符比较优先级,如果栈顶的大,则出栈,然后数据栈出两个,进行相应的运算,处于表达式中的操作符继续和栈顶的比较,如果处于表达式中的大,则入栈。直到扫描完,并且栈内无操作符为止。

 

 

#include "stack.h"                    /*将栈包涵进来*/

#include "stdlib.h"

#include "math.h"

#define N 5              /*预定义计算器函数*/

 

 

/*table 运算符优先级表*/

/*------------“+-*/()^#”---------------*/

int table[8][8] = {

       {1,1,-1,-1,-1,1,-1,1},

       {1,1,-1,-1,-1,1,-1,1},

       {1,1,1,1,-1,1,-1,1},

       {1,1,1,1,-1,1,-1,1},

       {-1,-1,-1,-1,-1,0,-1,0},

       {1,1,1,1,0,1,1,1},

       {1,1,1,1,-1,1,1,1,},

       {-1,-1,-1,-1,-1,-1,-1,0},

};

 

char * oper = "+-*/^";                        /*所实现的操作函数的功能*/

 

float add(float,float);

float sub(float,float);

float mul(float,float);

float di(float,float);

float power(float,float);

float (* fun[N])(float,float) = {add,sub,mul,di,power};

float ChangeToFloat(char ** pstart);                   /*将字符串转化成数字*/

int IsNum(char * pstart);                                      /*判断是否为数字*/

int IsOper(char * pstart);                                      /*判断是否为数字*/

int Compare(char ch1,char ch2);                          /*比较操作符的优先级*/

int SearchOper(char * str,char ch);                         /*查找是哪一个操作符,返回操作符的位置*/

 

main()

{

       Stack * pf;                     /*定义指向float数据的栈*/

       Stack * pc;                                      /*定义指向char数据的栈*/

       char str[25];                                     /*定义指向运算式的指针*/

       char ch = '#';                             /*初始化字符栈字符*/

       char * pstart;

       float ftemp1;

       float ftemp2;

 

       clrscr();

       printf("Yang PengFei: Mini Calculate 1.0 Version \nCopyright(C) 2010.");

       printf("\n\ninput a formula:");

       gets(str);

 

       pstart = str;

       pf = CreateStack(30,4);       /*创建float数据类型的栈*/

       pc = CreateStack(30,1);       /*创建char数据类型的栈*/

       Push(pc,&ch,1);              /*将'#'入栈,作为初始的工作*/

 

/*----------------------------初始化工作完成----------------------------------------------*/

       while(*pstart != '\0')

       {

              if(IsNum(pstart))

              {

                     ftemp1 = ChangeToFloat(&pstart);   /*完成此函数后,此时pstart指向数字的下一个操作符*/

                     Push(pf,&ftemp1,4);

              }

              else if(IsOper(pstart))

              {

                     if(*pstart == 40 && *(pstart + 1) == '-')       /*判断出现(-13),这样类型的数据,操作数一个,操作符也是一个,要做特殊处理,即在之前将0 压入数据栈*/

                     {

                            ftemp1 = 0;

                            Push(pf,&ftemp1,4);

                     }

                     GetTop(pc,&ch,1);                /*ch 为栈内的元素*/

                     if(Compare(*pstart,ch) == 1)     /*栈内的操作符优先级大*/

                     {

                            Pop(pc,&ch,1);

                            Pop(pf,&ftemp1,4);

                            Pop(pf,&ftemp2,4);

                            ftemp1 = fun[SearchOper(oper,ch)](ftemp1,ftemp2);

                            Push(pf,&ftemp1,4);

                     }

                     else if(Compare(*pstart,ch) == 0)

                     {

                            Pop(pc,&ch,1);

                            pstart++;

                     }

                     else

                     {

                            Push(pc,pstart,1);

                            pstart++;

                     }

              }

       }

 

 

/*表达式已经扫描完毕,但是栈内有可能还有数据和操作符,做最后的处理*/

 

       Pop(pc,&ch,1);

       while(ch != '#' && !IsEmpty(pf))

       {

              Pop(pf,&ftemp1,4);

              if(!Pop(pf,&ftemp2,4))

              {

                     printf("Sorry!Maybe the string you input is error,please cheack!");

                     break;

              }

              ftemp1 = fun[SearchOper(oper,ch)](ftemp1,ftemp2);

              Push(pf,&ftemp1,4);

              Pop(pc,&ch,1);

       }

 

       if(ch == '#')

              printf("The result is:%.2f",ftemp1);

 

       getch();

}

float ChangeToFloat(char ** pstart)

{

       float ftemp;

       char a[10];

       int i = 0;

 

       while(!IsOper(*pstart) && **pstart != '\0')

       {

              a[i] = **pstart;

              (*pstart)++;

              i++;

       }

       a[i] = '\0';

       ftemp = atof(a);                                /*调用的系统提供的字符串转换函数*/

       return ftemp;

}

int IsNum(char * pstart)

{

       if(*pstart == 46 || (*pstart >= 48 && * pstart <= 57))

              return 1;

       return 0;

}

int IsOper(char * pstart)

{

       if(*pstart == 40 || *pstart == 41 || *pstart == 42 || *pstart == 43 || *pstart == 45 || *pstart == 47 || *pstart == 94)

              return 1;

       return 0;

}

int Compare(char ch1,char ch2)            /*ch1为算式内的操作符,ch2为栈内操作符*/

{

       char * a = "+-*/()^#";

       return table[SearchOper(a,ch2)][SearchOper(a,ch1)];

}

int SearchOper(char * str,char ch)

{

       int i = 0;

       while(*str != ch)

       {

              str++;

              i++;

       }

       return i;

}

float add(float a,float b)

{

       return a + b;

}

float sub(float a,float b)

{

       return b - a;

}

float mul(float a,float b)

{

       return a * b;

}

float di(float a,float b)

{

       return b / a;

}

float power(float a,float b)

{

       return pow(b,a);

}

 

 

       总结:

posted on 2012-07-21 18:58  小杨同志  阅读(257)  评论(0编辑  收藏  举报