c语言强化训练——简易计算器

来源:互联网 发布:外国电视台收看软件 编辑:程序博客网 时间:2024/05/16 11:08

 

一、设计要求

实现一个简单的计算器,要求可以求解表达式,支持基本的运算并有扩展能力和基本的容错能力

 

二、设计思路

程序需要定义两个工作栈,分别保存表达式计算过程中的运算符与运算数,通过一个优先级表来判定运算顺序。通过判定输入的运算符来调用不同的函数,实现支持基本的运算符号。可以通过定义一个运算符表和一个函数指针表,通过查找方式调用函数,实现运算符的可扩展性。

 

三、详细设计

1、首先需要编写一个栈,这个栈需要支持浮点数和字符,编写这个栈stack.h文件

typedef struct {        char * buffer;        int typesize;        int top;        int max;} Stack;Stack * CreateStack(int max, int typesize);void DestroyStack(Stack *);void ClearStack(Stack *);int Push(Stack *, void *);int Pop(Stack *, void *);int GetElement(Stack*, unsigned int n, void *);int GetTop(Stack *, void *);int IsEmpty(Stack *);int IsFull(Stack *);Stack * CreateStack(int max, int typesize){        Stack * s = (Stack*)malloc(sizeof(Stack));        if (!s) return 0;        s->buffer = (char *)malloc(sizeof(char) * max * typesize);        if (!s->buffer) return 0;        s->top = -1;        s->max = max;        s->typesize = typesize;        return s;}void DestroyStack(Stack* s){        free(s->buffer);        free(s);}void ClearStack(Stack * s){        s->top = -1;}int Push(Stack * s, void * data){        if (IsFull(s)) return 0;        s->top++;        memcpy(s->buffer + s->top*s->typesize, data, s->typesize);        return 1;}int Pop(Stack * s, void * data){        if (IsEmpty(s)) return 0;        memcpy(data, s->buffer + s->top*s->typesize, s->typesize);        s->top--;        return 1;}int GetTop(Stack *s, void * data){        if (IsEmpty(s)) return 0;                memcpy(data, s->buffer + s->top*s->typesize, s->typesize);                return 1;}int IsEmpty(Stack * s){        return s->top == -1;}int IsFull(Stack * s){        return s->top == s->max-1;}

 

2、下面编写calcu.c文件,实现计算器功能,首先引入需要的头文件

#include "stdlib.h"#include "stack.h"

3、对于用户输入的字符串要进行解析成运算符和数值,可以通过一个数组A来保存,再定义一个标记位数组B,标记数组A中哪一位是数据,哪一位是运算符。为了更明确数据与标记位的关系,这里把二者定义成一个结构体,通过结构体数据接受解析结果

#define fldouble 0#define flchar 1typedef struct {        double data;        char flag;} cal;

4、定义支持的运算符,通过一个函数查找某个字符在运算符表中的位置,若不存在则返回-1

int SearchCode(char ch){        char * code = "+-*/^()#";        int n  = 0;        while (code[n])        {                if (code[n] == ch) return n;                n++;        }        return -1;}

5、定义优先级表,控制运算过程中运算顺序,传入的参数为sch:栈中字符,nch下一个字符,返回优先级顺序

char GetPerferen(char sch, char nch){        char perferen[8][8] = {                ">><<<<>>",                ">><<<<>>",                ">>>><<>>",                ">>>><<>>",                ">>>>><>>",                "<<<<<<=E",                ">>>>>E>>",                "<<<<<<E="        };        int s = SearchCode(sch);        int n = SearchCode(nch);        if (s==-1 || n==-1) return 'E';        return perferen[s][n];} rs;}double calcu(double a, char ch, double b){        double (*function[5])(double,double) = {add,sub,mul,ddiv,mi};        return function[SearchCode(ch)](a,b);}

6、定义支持的运算函数,和一个查找函数指针列表的函数,函数指针列表中的运算函数与运算符表一一对应

double add(double a, double b) { return a+b; }double sub(double a, double b) { return a-b; }double mul(double a, double b) { return a*b; }double ddiv(double a, double b) { return a/b; }double mi(double a, double b){        int n = 1;        double rs = a;        for (n=1; nreturn

7、编写函数检查用户输入的字符串是否合法,若含有结束符#,或不是有效运算符,不是数字且不是小数点,则视为非法字符,返回0。若字符串合法,在字符串末尾加入运算结束符#

int CheckStr(char * buffer){        int n = -1;        while (buffer[++n])        {                if ((SearchCode(buffer[n]) != -1 ||                         buffer[n] == '.' ||                          (buffer[n] >= '0' && buffer[n] <= '9'))                           && buffer[n] != '#')                        continue;                else                        return 0;        }        buffer[n] = '#';        buffer[n+1] = 0;        return 1;}

8、解析过程是遍历字符串的过程,首先判断当前字符是否为运算符,若不是运算符调用一个函数GetNextValue获取这个数值。若是运算符但为加号或减号,需要判断是正负号还是加减号,再分别处理

首先编写GetNextValue函数,这个函数需要字符串指针,和指向解析过程中计数器的指针,接收结果的指针。若数值中有多个小数点或者小数点出现在第一位或最后一位,则非法,返回0,成功则返回1

int GetNextValue(char * buffer, int * n, double * rs){        char str[30];        int ii=0,ij = 0;        while (SearchCode(buffer[*n]) == -1)        {                str[ii++] = buffer[*n];                (*n)++;        }        str[ii] = 0;        ii=0;        while (str[ii])        {                if (str[ii++] == '.') ij++;        }        if (ij>1 || str[ii-1] == '.' || str[0] == '.') return 0;        *rs = atof(str);        return 1;}

9、解析函数需要传入用户输入的字符串指针和接受解析结果的指针,成功返回0,失败返回1

int resolu(char * buffer, cal * calstr){        int bn = 0, cn = 0;        cal c;        while (buffer[bn])        {                if (SearchCode(buffer[bn]) == -1)                {                        if (!GetNextValue(buffer,&bn, &c.data)) return 0;                        c.flag = fldouble;                        calstr[cn++] = c;                }                else                {                        if ((buffer[bn] == '-' && buffer[bn-1] == '(') || (bn==0 && buffer[0] == '-'))                        {                        bn++;                                if (!GetNextValue(buffer,&bn, &c.data)) return 0;                                c.data = 0 - c.data;                                c.flag = fldouble;                                calstr[cn++] = c;                        }                        if ((buffer[bn] == '+' && buffer[bn-1] == '(') || (bn==0 && buffer[0] == '+'))                        {                        bn++;                                if (!GetNextValue(buffer, &bn, &c.data)) return 0;                                c.flag = fldouble;                                calstr[cn++] = c;                        }                        else                        {                                c.data = (double)buffer[bn++];                                c.flag = flchar;                                calstr[cn++] = c;                        }                                        }        }}

10、运算函数

运算过程需要两个工作栈,当符号栈顶和解析串的下一个字符均为#时,表示运算结束。结束后数值栈应该只有一个元素,这个元素就是结果。这个函数需要传入指向解析结果的指针,和一个接收运算结果的指针,返回计算是否成功

int result(cal * calstr, double * rs){        Stack * pst = CreateStack(100,sizeof(char));        Stack * pnd = CreateStack(100,sizeof(double));        double num1,num2;        int n = 0;        char ch = '#';        Push(pst, &ch);        while(ch != '#' || !(calstr[n].flag == flchar && (char)calstr[n].data == '#'))        {                if (calstr[n].flag == fldouble)                {                        Push(pnd, &(calstr[n].data));                        n++;                }                else                {                        switch( GetPerferen(ch, (char)calstr[n].data))                        {                        case '<':                                ch = (char)calstr[n].data;                                Push(pst, &ch);                                n++;                                break;                        case '=':                                if (!Pop(pst, &ch)) return 0;;                                n++;                                break;                        case '>':                                if (!(Pop(pnd,&num2) && Pop(pst,&ch) && Pop(pnd,&num1))) return 0;;                                num1 = calcu(num1,ch,num2);                                Push(pnd, &num1);                                break;                        case 'E':                                return 0;                        }                }                if (!GetTop(pst, &ch)) return 0;        }        if (GetSize(pnd) == 1 && GetTop(pnd,rs))        {                DestroyStack(pst);                DestroyStack(pnd);                return 1;        }        else        {                return 0;        }}

11、编写与用户交互的函数

void treatment(){        char buffer[100];        cal calstr[50];        double rs;        printf("calcu>");        gets(buffer);        while (!(buffer[0]=='e' && buffer[1]=='x' && buffer[2]=='i' && buffer[3]=='t'))        {                if (CheckStr(buffer) && resolu(buffer,calstr) && result(calstr,&rs))                {                        printf("/n%f/n",rs);                }                else                {                        printf("/nError!/n");                }                printf("/ncalcu>");                gets(buffer);        }        printf("/nbye/n");}

 

12、主函数

main(){        printf("Copyright/n/n");        treatment();}

四、测试

程序在TC2.0环境下测试通过

 

        (I)正常检测:
                (1)运算情况是否正确检测:
                          1.12+3*(3*4^3+3.12542*(1+2))/(4.2-1)+2.1 = 192.01024375
 
               (2)负数运算检测:
                         -2.1+((-2)^2)= 1.9

               (3)正数运算检测:
                         +2.1+((+2)^2)
              
               (4)单数字输入运算检测:
                         1  = 1.00000
            

        (II)容错检测:
                 (1)小数点输入错误检测:
                           1.22.+3..234..
                            .1+2

                (2)括号是否匹配检测:
                          ((2+3)/(5/4.1)))
 
                (3)运算符输入错误检测:
                          2.1--2
                          2.1++2
                          2.1**2
                          2.1//2
                          2.1^^2

                (4)字符串输入错误检测:
                          1+3.1+5=
                          1#3.3
                           #                          
                           (())
                           回车
                           空格