一、前言与简介:

这次的学习目标是寻找一个已有的软件项目,分析阅读,找出里面存在的缺陷,改进其软件进行二次开发,本篇博客记录了这次学习的过程。

在这次学习中,寻找的软件项目是一个用C语言实现的计算器,其功能为输入一行以'#"号结尾的四则运算算式,允许带有括号和小数点,能够输出结果。

Java项目二次开发和一次开发 程序二次开发_Java项目二次开发和一次开发

Java项目二次开发和一次开发 程序二次开发_Java项目二次开发和一次开发_02

 

这个项目中没有缺陷,因此对其的再开发只有添加的新功能,具体是添加了次方运算的功能,使计算器能够输出正确的结果。

 

二、原项目总体介绍

在原软件项目中,涉及到的知识点有中缀表达式转后缀表达式和逆波兰计算法。

具体的实现思路是:

输入一个表达式,扫描该表达式,遇到数直接压入数字栈,遇到符号压入符号栈(判断优先级)。符号栈弹出符号的时候,拿数字栈上边两个元素做该符号的运算,计算出的结果再次压入数字栈。直到scanf 读取到 '#' 时候,扫描结束,此时把栈内剩余的符号弹出,再次进行计算,最后放在数字栈栈底的元素就是该表达式的结果。

原软件项目的全部源代码:

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
 
#define OK              1
#define DEFEAT          0
#define STACK_INIT_SIZE 15
 
typedef int Status;
 
// 这个栈存放运算符
typedef struct sqStack
{
    char *top;
    char *base;
    int stack_size;
} sqStack;
 
// 这个栈存放待运算的数
typedef struct resStack
{
    double *top;
    double *base;
    int stack_size;
} resStack;
 
void initSqStack(sqStack *stack);
void initResStack(resStack *stack);
void ePush(sqStack *symbol_stack, char e);
void resStackCalc(resStack *res_stack, char e);
void point_cnn(resStack *res_stack, char e, int cnt);
void highPriority(sqStack *symbol_stack, resStack *res_stack);
void destroyStack(sqStack *symbol_stack, resStack *res_stack);
void res_stack_push(resStack *res_stack, char e, char previous);
void divideStack(sqStack *symbolStack, resStack *res_stack, char e);
void lowPriority(sqStack *symbol_stack, resStack *res_stack,char e);
void mediumPriority(sqStack *symbol_stack, resStack *res_stack, char e);
void symbolPriority(sqStack *symbol_stack, resStack *res_stack, char e);
void symbol_stack_push(sqStack *symbol_stack, resStack *res_stack, char e);
 
// 初始化符号栈
void initSqStack(sqStack *stack)
{
    stack->base = (char *)malloc(STACK_INIT_SIZE * sizeof(char));
    if (!stack->base)
    {
        printf("sqStack初始化失败!\n");
        exit(1);
    }
    stack->top = stack->base;
    stack->stack_size = STACK_INIT_SIZE;
}
 
// 初始化数字栈
void initResStack(resStack *stack)
{
    stack->base = (double *)malloc(STACK_INIT_SIZE * sizeof(double));
    if (!stack->base)
    {
        printf("resStack初始化失败!\n");
        exit(1);
    }
    stack->top = stack->base;
    stack->stack_size = STACK_INIT_SIZE;
}
 
// 区分符号与数字
void divideStack(sqStack *symbol_stack, resStack *res_stack, char e)
{
    static Status flag = 0;     // 记录前边有没有小数点
    static int cnt = 0;         // 记录小数点后数字的个数
    static char previous;       // 记录上一个字符
    if (e == 46)
    {
        flag = 1;
        return;
    }
    if (e >= 48 && e <= 57)
    {
        if (flag)   // 如果为 1 则前面有小数点。
        {
            cnt ++;
            point_cnn(res_stack, e, cnt);
        }
        else
        {
            res_stack_push(res_stack, e, previous);
        }
    }
    else
    {
        flag = cnt = 0;
        symbol_stack_push(symbol_stack, res_stack, e);
    }
    previous = e;
}
 
// 把能直接压栈的情况全写在这里
void symbol_stack_push(sqStack *symbol_stack, resStack *res_stack, char e)
{
    // 如果栈为空或者当前符号为左括号,则直接压栈
    if (symbol_stack->top == symbol_stack->base || e == '(')
    {
        *(symbol_stack->top) = e;
        symbol_stack->top ++;
    }
    // 如果栈顶符号为左括号,则直接压栈
    else if (*(--symbol_stack->top) == '(')
    {
        symbol_stack->top ++;
        *(symbol_stack->top) = e;
        symbol_stack->top ++;
    }
    else
    {
        symbol_stack->top ++;
        symbolPriority(symbol_stack, res_stack, e);
    }
}
 
// 判断符号优先级
void symbolPriority(sqStack *symbol_stack, resStack *res_stack, char e)
{
    /* 
        既然已经进到 symbolPriority 函数了,
        那就说明栈不为空,栈顶符号不为左括号, 
        当前符号不是左括号
    */
    if (e == '+' || e == '-')
    {
        lowPriority(symbol_stack, res_stack, e);
    }
    else if (e == '*' || e == '/')
    {
        mediumPriority(symbol_stack, res_stack, e);
    }
    else    // e 为 右括号的情况
    {
        highPriority(symbol_stack, res_stack);
    }
}
 
// 清空括号内的符号
void highPriority(sqStack *symbol_stack, resStack *res_stack)
{
    do
    {
        symbol_stack->top --;
        if (*(symbol_stack->top) == '(')
        {
            break;
        }
        resStackCalc(res_stack, *(symbol_stack->top));
    } while (symbol_stack->top != symbol_stack->base);
}
 
// 高优先级符号入栈
void mediumPriority(sqStack *symbol_stack, resStack *res_stack, char e)
{
    do
    {
        symbol_stack->top --;
        if (*(symbol_stack->top) == '(')
        {
            break;
        }
        // 防止栈底符号被覆盖
        if (*(symbol_stack->top) == '+' || *(symbol_stack->top) == '-')
        {
            symbol_stack->top ++;
            *(symbol_stack->top) = e;
            symbol_stack->top ++;
            return;
        }
        resStackCalc(res_stack, *(symbol_stack->top));
    } while (symbol_stack->top != symbol_stack->base);
 
    // 将优先级大于等于 e 的弹出之后压入栈中。
    ePush(symbol_stack, e);
}
 
// 低优先级符号入栈
void lowPriority(sqStack *symbol_stack, resStack *res_stack, char e)
{
    do
    {
        symbol_stack->top --;
        if (*(symbol_stack->top) == '(')
        {
            break;
        }
        resStackCalc(res_stack, *(symbol_stack->top));
    } while (symbol_stack->top != symbol_stack->base);
 
    // 将优先级大于等于 e 的弹出之后压入栈中。
    ePush(symbol_stack, e);
}
 
/* 
    把栈内优先级大于等于e的符号弹完后,进入这个函数只有两种情况,
    1. 遇到左括号  (需要top指针先++ 再压栈)
    2. 到栈底了    (直接压栈)
*/
void ePush(sqStack *symbol_stack, char e)
{
    if (symbol_stack->top == symbol_stack->base)    // 栈底
    {
        *(symbol_stack->top) = e;
        symbol_stack->top ++;
    }
    else                                            // 左括号
    {
        symbol_stack->top ++;
        *(symbol_stack->top) = e;
        symbol_stack->top ++;
    }
}
 
// 执行运算
void resStackCalc(resStack *res_stack, char e)
{
    // 计算的时候是用 下边的数 运算符 上边的数
    double b = *(--res_stack->top);
    double a = *(--res_stack->top);
    switch (e)
    {
        case '+': *(res_stack->top) = a+b; break;
        case '-': *(res_stack->top) = a-b; break;
        case '*': *(res_stack->top) = a*b; break;
        case '/': *(res_stack->top) = a/b; break;
    }
    // 压栈完毕之后, top指针需要往上走一步
    res_stack->top ++;
}
 
// 把数字压入栈中
void res_stack_push(resStack *res_stack, char e, char previous)
{
    if (res_stack->top == res_stack->base)
    {
        *(res_stack->top) = e - 48;
        res_stack->top ++;
        return;
    }
    if (previous >= 48 && previous <= 57)   // 上一个如果是个数,则进行拼接
    {
        double sum = *(--res_stack->top) * 10 + (e - 48);
        *(res_stack->top) = sum;
        res_stack->top ++;
    }
    else
    {
        // 如果上一个不是数字则直接压栈
        *(res_stack->top) = (e - 48);
        res_stack->top ++;
    }
}
 
// 拼接小数点后边的数字
void point_cnn(resStack *res_stack, char e, int cnt)
{
    double n = e - 48;
    double cnnt = *(--res_stack->top) + n/pow(10, cnt);
    *(res_stack->top) = cnnt;
    res_stack->top ++;
}
 
// 把符号栈内剩余的符号弹出, 进行计算
void clearSymbolStack(sqStack *symbol_stack, resStack *res_stack)
{
    do
    {
        symbol_stack->top --;
        resStackCalc(res_stack, *(symbol_stack->top));
    } while (symbol_stack->top != symbol_stack->base);
}
 
// 销毁栈
void destroyStack(sqStack *symbol_stack, resStack *res_stack)
{
    free(symbol_stack->base);
    free(res_stack->base);
    // 让指针指向NULL, 防止野指针出现
    symbol_stack->base = symbol_stack->top = NULL;
    res_stack->base = res_stack->top = NULL;
    symbol_stack->stack_size = res_stack->stack_size = 0;
}
 
int main(void)
{
    char input;
    sqStack symbol_stack;
    resStack res_stack;
 
    initResStack(&res_stack);
    initSqStack(&symbol_stack);
 
    printf("输入中缀表达式:");
    do
    {
        scanf("%c", &input);
        if (input != '#')
        {
            divideStack(&symbol_stack, &res_stack, input);
        }
    } while (input != '#');
 
    getchar();  // 清空缓冲区
    clearSymbolStack(&symbol_stack, &res_stack);
 
    printf(">> %f", *(res_stack.base));    // 此时数字栈栈底就是结果
 
    destroyStack(&symbol_stack, &res_stack);
    return 0;
}
//————————————————
//版权声明:本文为CSDN博主「小摆子*^_^*」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。

 

在本次学习过程中,我采用了在纸上演算例子的方式来理解原程序,下面是阅读理解的过程:

Java项目二次开发和一次开发 程序二次开发_Java项目二次开发和一次开发_03

Java项目二次开发和一次开发 程序二次开发_运算符_04

 

Java项目二次开发和一次开发 程序二次开发_运算符_05

Java项目二次开发和一次开发 程序二次开发_入栈_06

 

Java项目二次开发和一次开发 程序二次开发_Java项目二次开发和一次开发_07

Java项目二次开发和一次开发 程序二次开发_优先级_08

 

 

 

Java项目二次开发和一次开发 程序二次开发_Java项目二次开发和一次开发_09

Java项目二次开发和一次开发 程序二次开发_优先级_10

 

 

 

Java项目二次开发和一次开发 程序二次开发_运算符_11

Java项目二次开发和一次开发 程序二次开发_优先级_12

 

全过程图解:

 

Java项目二次开发和一次开发 程序二次开发_入栈_13

Java项目二次开发和一次开发 程序二次开发_入栈_14

在阅读理解之后,我对这个C语言编写的计算器有了更深入的了解,下面,将对每个函数的功能用自己的语言进行概括

void initSqStack(sqStack *stack);//初始化符号栈,没啥值得讲的
void initResStack(resStack *stack);//初始化数字栈,没啥值得讲的
void ePush(sqStack *symbol_stack, char e);
/*
将新的运算符压入栈,原注释写到:走到这里是遇到了左括号或到了栈底,这里实际上需要我们关注的是“遇到了左括号”的这种情况,
后面的三种Priority函数功能是负责将运算符“入栈”——“运算”——“弹出”,同时其运算自带清除栈中积压的运算符功能,
因此在程序中许多情况是左括号之上的运算符清空了之后,
新的运算符遇到左括号进入此函数而再次被压入栈的(至于为什么要清空左括号上面,是因为左右括号比加减乘除的优先级高),
而到栈底的情况自然没什么好讲的
*/
void resStackCalc(resStack *res_stack, char e);//用 下边的数 运算符 上边的数,没有太难的地方
void point_cnn(resStack *res_stack, char e, int cnt);//小数点的处理,不难理解
void highPriority(sqStack *symbol_stack, resStack *res_stack);//右括号是最高优先级,自带清除积压功能
void destroyStack(sqStack *symbol_stack, resStack *res_stack);//销毁栈,没啥好讲的
void res_stack_push(resStack *res_stack, char e, char previous);//将数字压入数字栈,没有太难的地方
void divideStack(sqStack *symbolStack, resStack *res_stack, char e);//对数字入栈和运算符入栈的区分处理
void lowPriority(sqStack *symbol_stack, resStack *res_stack,char e);//加减,低优先级,自带清除积压功能
void mediumPriority(sqStack *symbol_stack, resStack *res_stack, char e);//乘除,中优先级,自带清除积压功能
void symbolPriority(sqStack *symbol_stack, resStack *res_stack, char e);//判断运算符的优先级,不同优先级运算符入栈时的具体处理是不一样的
void symbol_stack_push(sqStack *symbol_stack, resStack *res_stack, char e);//将符号压入符号栈,按实际情况直接压入,或激活其清除积压运算符的功能
void clearSymbolStack(sqStack *symbol_stack, resStack *res_stack)// 把符号栈内剩余的符号弹出, 进行计算

上面对每个函数的功能尝试进行了概括,但是发现单纯的文字描述函数功能效果并不理想,只是稍详细地归纳了一个函数。此外原程序的重难点也值得进一步的研究。由于本次学习任务时间较紧,能力有限,因此其余的原程序重难点在此只是列举出来一部分,没有深入地进行探究。

重难点1:右括号与左括号的对接处理

涉及到的函数:

void highPriority(sqStack *symbol_stack, resStack *res_stack);//右括号是最高优先级

重难点2:剩余符号的清空

涉及到的函数:

void clearSymbolStack(sqStack *symbol_stack, resStack *res_stack)// 把符号栈内剩余的符号弹出, 进行计算

 

 

三、开发要点分析

在上个部分中,对程序总体进行了介绍,在对每个函数的功能尝试概括时,发现单纯的文字描述效果并不是非常理想。因此,准备采用文字与图形结合的方式对本项目的开发要点进行分析。添加的次方的运算功能就是建立在理解这一要点的基础之上的。这个要点,就是加减与乘除的不同优先级处理

分析所要涉及到的函数:

// 高优先级符号入栈
void mediumPriority(sqStack *symbol_stack, resStack *res_stack, char e)
{
    do
    {
        symbol_stack->top --;
        if (*(symbol_stack->top) == '(')
        {
            break;
        }
        // 防止栈底符号被覆盖
        if (*(symbol_stack->top) == '+' || *(symbol_stack->top) == '-')
        {
            symbol_stack->top ++;
            *(symbol_stack->top) = e;
            symbol_stack->top ++;
            return;
        }
        resStackCalc(res_stack, *(symbol_stack->top));
    } while (symbol_stack->top != symbol_stack->base);
 
    // 将优先级大于等于 e 的弹出之后压入栈中。
    ePush(symbol_stack, e);
}
 
// 低优先级符号入栈
void lowPriority(sqStack *symbol_stack, resStack *res_stack, char e)
{
    do
    {
        symbol_stack->top --;
        if (*(symbol_stack->top) == '(')
        {
            break;
        }
        resStackCalc(res_stack, *(symbol_stack->top));
    } while (symbol_stack->top != symbol_stack->base);
 
    // 将优先级大于等于 e 的弹出之后压入栈中。
    ePush(symbol_stack, e);
}

 

Java项目二次开发和一次开发 程序二次开发_优先级_15

 

 

Java项目二次开发和一次开发 程序二次开发_入栈_16

 

 

四、新功能的实现

 思路:次方的优先级是比乘除还要高的,但是比括号低,因此具体设计如下:

新添加一个函数:

 

void m_h_Priority(sqStack *symbol_stack, resStack *res_stack, char e)
{
    do
    {
        symbol_stack->top --;
        if (*(symbol_stack->top) == '(')
        {
            break;
        }
        // 防止栈底符号被覆盖
        if (*(symbol_stack->top) == '+' || *(symbol_stack->top) == '-'||*(symbol_stack->top) == '*'||*(symbol_stack->top) == '/')
        {
            symbol_stack->top ++;
            *(symbol_stack->top) = e;
            symbol_stack->top ++;
            return;
        }
        //printf("resStackCalc外的准备进入resStackCalc\n");
        resStackCalc(res_stack, *(symbol_stack->top));
        //printf("resStackCalc外的已经退出resStackCalc\n");
    } while (symbol_stack->top != symbol_stack->base);
 
    // 将优先级大于等于 e 的弹出之后压入栈中。
    ePush(symbol_stack, e);
}

 其他对应有变动的函数:

void symbolPriority(sqStack *symbol_stack, resStack *res_stack, char e)
{
    /* 
        既然已经进到 symbolPriority 函数了,
        那就说明栈不为空,栈顶符号不为左括号, 
        当前符号不是左括号
    */
    if (e == '+' || e == '-')
    {
        lowPriority(symbol_stack, res_stack, e);
    }
    else if (e == '*' || e == '/')
    {
        mediumPriority(symbol_stack, res_stack, e);
    }
    else if (e=='^')
    {
        m_h_Priority(symbol_stack,res_stack,e);
    }
    else    // e 为 右括号的情况
    {
        highPriority(symbol_stack, res_stack);
    }
}
void resStackCalc(resStack *res_stack, char e)
{
    // 计算的时候是用 下边的数 运算符 上边的数
    double b = *(--res_stack->top);
    double a = *(--res_stack->top);
    printf("%f %c %f\n",a,e,b);
    switch (e)
    {
        case '+': *(res_stack->top) = a+b; 
        //printf("*(res_stack->top)=%f\n",*(res_stack->top));
        break;
        case '-': *(res_stack->top) = a-b; break;
        case '*': *(res_stack->top) = a*b; break;
        case '/': *(res_stack->top) = a/b; break;
        case '^': *(res_stack->top) = pow(a,b);break;                          //
    }
    // 压栈完毕之后, top指针需要往上走一步
    res_stack->top ++;
}

 

五、测试与结果验证

Java项目二次开发和一次开发 程序二次开发_运算符_17

Java项目二次开发和一次开发 程序二次开发_入栈_18

 


Java项目二次开发和一次开发 程序二次开发_入栈_19

 

 

Java项目二次开发和一次开发 程序二次开发_优先级_20

Java项目二次开发和一次开发 程序二次开发_运算符_21

 

 二次开发后的全套源代码:

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
 
#define OK              1
#define DEFEAT          0
#define STACK_INIT_SIZE 15
 
typedef int Status;
 
// 这个栈存放运算符
typedef struct sqStack
{
    char *top;
    char *base;
    int stack_size;
} sqStack;
 
// 这个栈存放待运算的数
typedef struct resStack
{
    double *top;
    double *base;
    int stack_size;
} resStack;
 
void initSqStack(sqStack *stack);
void initResStack(resStack *stack);
void ePush(sqStack *symbol_stack, char e);
void resStackCalc(resStack *res_stack, char e);
void point_cnn(resStack *res_stack, char e, int cnt);
void highPriority(sqStack *symbol_stack, resStack *res_stack);
void destroyStack(sqStack *symbol_stack, resStack *res_stack);
void res_stack_push(resStack *res_stack, char e, char previous);
void divideStack(sqStack *symbolStack, resStack *res_stack, char e);
void lowPriority(sqStack *symbol_stack, resStack *res_stack,char e);
void mediumPriority(sqStack *symbol_stack, resStack *res_stack, char e);
void m_h_Priority(sqStack *symbol_stack, resStack *res_stack, char e);
void symbolPriority(sqStack *symbol_stack, resStack *res_stack, char e);
void symbol_stack_push(sqStack *symbol_stack, resStack *res_stack, char e);
 
// 初始化符号栈
void initSqStack(sqStack *stack)
{
    stack->base = (char *)malloc(STACK_INIT_SIZE * sizeof(char));
    if (!stack->base)
    {
        printf("sqStack初始化失败!\n");
        exit(1);
    }
    stack->top = stack->base;
    stack->stack_size = STACK_INIT_SIZE;
}
 
// 初始化数字栈
void initResStack(resStack *stack)
{
    stack->base = (double *)malloc(STACK_INIT_SIZE * sizeof(double));
    if (!stack->base)
    {
        printf("resStack初始化失败!\n");
        exit(1);
    }
    stack->top = stack->base;
    stack->stack_size = STACK_INIT_SIZE;
}
 
// 区分符号与数字
void divideStack(sqStack *symbol_stack, resStack *res_stack, char e)
{
    static Status flag = 0;     // 记录前边有没有小数点
    static int cnt = 0;         // 记录小数点后数字的个数
    static char previous;       // 记录上一个字符
    if (e == 46)
    {
        flag = 1;
        return;
    }
    if (e >= 48 && e <= 57)
    {
        if (flag)   // 如果为 1 则前面有小数点。
        {
            cnt ++;
            point_cnn(res_stack, e, cnt);
        }
        else
        {
            res_stack_push(res_stack, e, previous);
        }
    }
    else
    {
        flag = cnt = 0;
        symbol_stack_push(symbol_stack, res_stack, e);
    }
    previous = e;
    //printf("%f\n",*(res_stack->top));
}
 
// 把能直接压栈的情况全写在这里
void symbol_stack_push(sqStack *symbol_stack, resStack *res_stack, char e)
{
    // 如果栈为空或者当前符号为左括号,则直接压栈
    if (symbol_stack->top == symbol_stack->base || e == '(')
    {
        *(symbol_stack->top) = e;
        symbol_stack->top ++;
    }
    // 如果栈顶符号为左括号,则直接压栈
    else if (*(--symbol_stack->top) == '(')
    {
        symbol_stack->top ++;
        *(symbol_stack->top) = e;
        symbol_stack->top ++;
    }
    else
    {
        symbol_stack->top ++;
        symbolPriority(symbol_stack, res_stack, e);
    }
}
 
// 判断符号优先级
void symbolPriority(sqStack *symbol_stack, resStack *res_stack, char e)
{
    /* 
        既然已经进到 symbolPriority 函数了,
        那就说明栈不为空,栈顶符号不为左括号, 
        当前符号不是左括号
    */
    if (e == '+' || e == '-')
    {
        lowPriority(symbol_stack, res_stack, e);
    }
    else if (e == '*' || e == '/')
    {
        mediumPriority(symbol_stack, res_stack, e);
    }
    else if (e=='^')
    {
        m_h_Priority(symbol_stack,res_stack,e);
    }
    else    // e 为 右括号的情况
    {
        highPriority(symbol_stack, res_stack);
    }
}
 
// 清空括号内的符号
void highPriority(sqStack *symbol_stack, resStack *res_stack)
{
    do
    {
        symbol_stack->top --;
        if (*(symbol_stack->top) == '(')
        {
            break;
        }
        //printf("resStackCalc外的准备进入resStackCalc\n");
        resStackCalc(res_stack, *(symbol_stack->top));
        //printf("resStackCalc外的已经退出resStackCalc\n");
    } while (symbol_stack->top != symbol_stack->base);
}
 
// 高优先级符号入栈
void mediumPriority(sqStack *symbol_stack, resStack *res_stack, char e)
{
    do
    {
        symbol_stack->top --;
        if (*(symbol_stack->top) == '(')
        {
            break;
        }
        // 防止栈底符号被覆盖
        if (*(symbol_stack->top) == '+' || *(symbol_stack->top) == '-')
        {
            symbol_stack->top ++;
            *(symbol_stack->top) = e;
            symbol_stack->top ++;
            return;
        }
        
        resStackCalc(res_stack, *(symbol_stack->top));
        
    } while (symbol_stack->top != symbol_stack->base);
 
    // 将优先级大于等于 e 的弹出之后压入栈中。
    ePush(symbol_stack, e);
}
 
 void m_h_Priority(sqStack *symbol_stack, resStack *res_stack, char e)
{
    do
    {
        symbol_stack->top --;
        if (*(symbol_stack->top) == '(')
        {
            break;
        }
        // 防止栈底符号被覆盖
        if (*(symbol_stack->top) == '+' || *(symbol_stack->top) == '-'||*(symbol_stack->top) == '*'||*(symbol_stack->top) == '/')
        {
            symbol_stack->top ++;
            *(symbol_stack->top) = e;
            symbol_stack->top ++;
            return;
        }
        //printf("resStackCalc外的准备进入resStackCalc\n");
        resStackCalc(res_stack, *(symbol_stack->top));
        //printf("resStackCalc外的已经退出resStackCalc\n");
    } while (symbol_stack->top != symbol_stack->base);
 
    // 将优先级大于等于 e 的弹出之后压入栈中。
    ePush(symbol_stack, e);
}
 
 
// 低优先级符号入栈
void lowPriority(sqStack *symbol_stack, resStack *res_stack, char e)
{
                                                                                // printf("标记1:低优先级符号入栈,%c\n",e);
    do
    {
        symbol_stack->top --;                                                   // printf("%c\n",*(symbol_stack->top));
        if (*(symbol_stack->top) == '(')
        {
                                                                                // printf("标记2:do while的break\n");
            break;
        }
        //printf("resStackCalc外的准备进入resStackCalc\n");
        resStackCalc(res_stack, *(symbol_stack->top));
       // printf("resStackCalc外的已经退出resStackCalc\n");5+(2+3^2)/(9-(5*6-4))
    } while (symbol_stack->top != symbol_stack->base);
 
    // 将优先级大于等于 e 的弹出之后压入栈中。
    ePush(symbol_stack, e);
}
 
/* 
    把栈内优先级大于等于e的符号弹完后,进入这个函数只有两种情况,
    1. 遇到左括号  (需要top指针先++ 再压栈)
    2. 到栈底了    (直接压栈)
*/
void ePush(sqStack *symbol_stack, char e)
{
    //printf("进入ePush\n");
    if (symbol_stack->top == symbol_stack->base)    // 栈底
    {
        *(symbol_stack->top) = e;
        symbol_stack->top ++;
    }
    else                                            // 左括号
    {
        symbol_stack->top ++;
        *(symbol_stack->top) = e;
        symbol_stack->top ++;
    }
    //printf("退出ePush\n");
}
 
// 执行运算
void resStackCalc(resStack *res_stack, char e)
{
    // 计算的时候是用 下边的数 运算符 上边的数
    double b = *(--res_stack->top);
    double a = *(--res_stack->top);
    printf("%f %c %f\n",a,e,b);
    switch (e)
    {
        case '+': *(res_stack->top) = a+b; 
        //printf("*(res_stack->top)=%f\n",*(res_stack->top));
        break;
        case '-': *(res_stack->top) = a-b; break;
        case '*': *(res_stack->top) = a*b; break;
        case '/': *(res_stack->top) = a/b; break;
        case '^': *(res_stack->top) = pow(a,b);break;                          //
    }
    // 压栈完毕之后, top指针需要往上走一步
    res_stack->top ++;
}
 
// 把数字压入栈中
void res_stack_push(resStack *res_stack, char e, char previous)
{
    if (res_stack->top == res_stack->base)
    {
        *(res_stack->top) = e - 48;
        res_stack->top ++;                    // printf(">> %f\n", *(res_stack->top));                //printf("%f\n",*(res_stack->top));
        return;
    }
    if (previous >= 48 && previous <= 57)   // 上一个如果是个数,则进行拼接
    {
        double sum = *(--res_stack->top) * 10 + (e - 48);
        *(res_stack->top) = sum;
        res_stack->top ++;                   //  printf(">> %f\n", *(res_stack->top));                //printf("%f\n",*(res_stack->top));
    }
    else
    {
        // 如果上一个不是数字则直接压栈
        *(res_stack->top) = (e - 48);
        res_stack->top ++;                   //  printf(">> %f\n", *(res_stack->top));                //printf("%f\n",*(res_stack->top));
    }
}
 
// 拼接小数点后边的数字
void point_cnn(resStack *res_stack, char e, int cnt)
{
    double n = e - 48;
    double cnnt = *(--res_stack->top) + n/pow(10, cnt);
    *(res_stack->top) = cnnt;
    res_stack->top ++;
}
 
// 把符号栈内剩余的符号弹出, 进行计算
void clearSymbolStack(sqStack *symbol_stack, resStack *res_stack)
{
    do
    {
        symbol_stack->top --;
        resStackCalc(res_stack, *(symbol_stack->top));
    } while (symbol_stack->top != symbol_stack->base);
}
 
// 销毁栈
void destroyStack(sqStack *symbol_stack, resStack *res_stack)
{
    free(symbol_stack->base);
    free(res_stack->base);
    // 让指针指向NULL, 防止野指针出现
    symbol_stack->base = symbol_stack->top = NULL;
    res_stack->base = res_stack->top = NULL;
    symbol_stack->stack_size = res_stack->stack_size = 0;
}
 
int main(void)
{
    char input;
    sqStack symbol_stack;
    resStack res_stack;
 
    initResStack(&res_stack);
    initSqStack(&symbol_stack);
 
    printf("输入中缀表达式:");
    do
    {
        scanf("%c", &input);
        //if(input=='s')//思路:一旦识别到了sqrt的s,可以将其先压入栈,并当作识别到了左括号,当遇到右括号算完之后,将其开方 
        if (input != '#')
        {
            divideStack(&symbol_stack, &res_stack, input);                   //printf("%f  %f\n",*(res_stack.top),*(res_stack.base));
        }
    } while (input != '#');
 
    getchar();  // 清空缓冲区
    clearSymbolStack(&symbol_stack, &res_stack);
 
    printf(">> %f", *(res_stack.base));    // 此时数字栈栈底就是结果
 
    destroyStack(&symbol_stack, &res_stack);
    return 0;
}

 

 

 

六、心得体会

1.虽然在之前的课程中接触过中缀表达式转后缀表达式和逆波兰计算法的内容,但却一直没有掌握,本次对该C语言的计算器进行二次开发,依然没有搞懂,但是通过样例演算运行的方式,让我对如何学习他人编写的软件项目有了更深的体会,对软件开发这一过程有所收获,自身也有所成长

2.本来还曾经设想过在计算器中加入开平方的算法,但是由于难度较高,时间仓促,所以放弃了,如果有时间的话,可以将其作为兴趣探究