C语言数据结构静态栈——计算器的实现

时间:2020-04-06
本文章向大家介绍C语言数据结构静态栈——计算器的实现,主要包括C语言数据结构静态栈——计算器的实现使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

C语言数据结构静态栈——计算器的实现:

下面我来详细的解释一下我编写的计算器代码。。。。
有必要先说明一下的是,此代码中含有两个栈:数据栈和算符栈

数据栈:存放double型数字;
算符栈:存放char型运算符(加、减、乘、除、括号)和起止标志符(#);

  #define maxsize 30 

/*数据栈*/
typedef struct Stack_F
{
    double data[maxsize];
    int top; //栈顶指针
}num;   //number 数字 

/*算符栈*/
typedef struct Stack_C
{
    char data[maxsize];
    int top; //栈顶指针
}sym;   //symbol 符号

关于栈的操作函数有如下几个:

/*数据栈初始化*/
void Initstack_num(num& N)
{
    N.top = 0;
}

/*算符栈初始化*/
void Initstack_sym(sym& S)
{
    S.top = 0;
}

/*数据栈压栈*/
void Pushstack_num(num& N, double e)
{
    N.data[N.top] = e;
    N.top++;
}

/*算符栈压栈*/
void Pushstack_sym(sym& S, char e)
{
    S.data[S.top] = e;
    S.top++;
}

/*数据栈出栈*/
double Popstack_num(num& N)
{
    N.top--;
    return N.data[N.top];
}

/*算符栈出栈*/
char Popstack_sym(sym& S)
{
    S.top--;
    return S.data[S.top];
}

接下来我们步入正题:
使用 gets_s() 在控制台上输入 字符串(算式),调用数据栈和算符栈初始化函数,再调用(分类函数)sort() ,把算式传到分类函数中进行数字和运算符的分类。
*话说:为啥会用到 gets_s() 而不是用 get();
(⊙﹏⊙),我也想用 get();但是 vs2019 编译器不允许········,度娘肯定知道啥子原因!!。。*

/*主函数*/
int main()
{
    void sort(num & N, sym & S, char str[]);
    void Initstack_num(num & N);
    void Initstack_sym(sym & S);
    char str[maxsize];//初始化字符串大小
    num N;
    sym S;
    printf("***********************************************************************************************************************\n");
    printf("*****************************************************栈式简单计算器****************************************************\n");
    printf("\n提示:输入的算式以 ‘#’ 符号为开始符,以  ‘#’ 符号为结束符,在算式中可以包含的运算有‘+’‘-’‘*’‘/’‘(’‘)’,输入完毕之后,直接回车即可\n ");
    printf("\n\n注意:运算符必须在英式状态下输入!!!");
    printf("\n\n\n输入算式示例:       #1+2#   ");
    printf("\n\n请输入所需要计算的算式:           ");
    gets_s(str);//输入字符串
    Initstack_num(N);
    Initstack_sym(S);
    sort(N, S, str);
    return 0;
}

下面才是计算器代码中的精华部分 ——sort()函数(对输入的算式进行分类),在进行解释之前呢,先把思路说一下:
1.凡是遇到数字直接压入数据栈。
2.
a、当遇到运算符(加、减、乘、除),先判断算符栈中是否为空:
若为空:二话不说直接把它压入算符栈。
若不为空:比较运算符优先级之后,判断是否压入算符栈;
b、 当遇到运算符(右括号),直接压入算符栈,因为左括号的优先级比加减乘除都高。
c、当遇到运算符(左括号),直接取出算符栈的运算符,直到匹配到左括号为止。
d、当遇到标志符(#),如果是第一个:直接压入算符栈,因为第一个#是标志的算式的开始,它必须进入算符栈;如果是最后一个,直接取出算符栈中的符号,直到匹配到 # 为止。

思路就是这个思路,但为了方便理解第1条中怎么把数字字符转换成数字,并将其压入数据栈,咱么先来认真分析 sort() 函数中的一部分代码:

int i = 0, j = 0,m=0;
char Buff[maxsize];//预存数字字符串
if (str[i] >= '0' && str[i] <= '9')//如果该字符为数字字符,则执行如下代码
        {

            while (str[i] >= '0' && str[i] <= '9')//如果该字符为数字字符,则进入while循环
            {
                Buff[j] = str[i];//把str[i]字符赋值给Buff[maxsize]数组
                j++;
                i++;
            }
            /*
            当上面的while循环退出之后,Buff数组中是存有数字字符的,用d指针指向数组下标为m时的位置,调用atof函数,
            从m位置(m初始化为0)开始,直到遇见数组中的‘\0’字符时,把这之间的数字字符转换成数字
            */
            d = &Buff[m];
            c = atof(d);//把Buff数组中的数字字符串转换为浮点型数据
            m = j;
            Pushstack_num(N, c);//把数字压入数据栈
        }

可能有一些媛媛(猿猿)不明白一个问题:
**d = &Buff[m];
c = atof(d);
m = j;**
这三句话啥子意思?为啥子这样写?

为了能更好的解决媛媛(猿猿)的疑惑,咱们来画图比较一下说明:

这个过程本猿就不在用文字讲述了,看图应该是看得懂的哇!!。 不信??。继续看。。。我相信你是一只聪明的猿猿(媛媛)呢!
讲到这里。猿猿(媛媛)们是否懂了呢?

对了,本猿这里有一张优先级对照表,猿猿(媛媛)可以参考一下:

好了,废话不多说,接下来上 sort() 函数代码:
里面每一部分都是有注释的,所以,我就不再用文字啰嗦了呢~~~~

/*分类函数*/
void sort(num& N, sym& S, char str[])
{
    void Pushstack_num(num & N, double e);
    void Pushstack_sym(sym & S, char e);
    double Popstack_num(num & N);
    char Popstack_sym(sym & S);
    double calculate(double a, double b, char n);
    int i = 0, j = 0,m=0;
    double a, b;//接收数据栈返回值
    double c;//暂时存储数字字符转换成浮点型数值
    char* d;//声明d指针,是为了后续调用atof函数:   double    __cdecl atof   (_In_z_ char const* _String);
    double f;//接收运算函数的返回值,将其压入数据栈
    char k;//接收str数组中一个临时字符
    char n;//接收算符栈的返回值
    double value;//最终算式的结果值
    char Buff[maxsize];//预存数字字符串
    while (str[i] != '\0')//判断第i个字符是否为截至字符,如果是,跳出while循环,如果不是,进入while循环
    {
        if (str[i] >= '0' && str[i] <= '9')//如果该字符为数字字符,则执行如下代码
        {

            while (str[i] >= '0' && str[i] <= '9')//如果该字符为数字字符,则进入while循环
            {
                Buff[j] = str[i];//把str[i]字符赋值给Buff[maxsize]数组
                j++;
                i++;
            }
            /*
            当上面的while循环退出之后,Buff数组中是存有数字字符的,用d指针指向数组下标为m时的位置,调用atof函数,
            从m位置(m初始化为0)开始,直到遇见数组中的‘\0’字符时,把这之间的数字字符转换成数字
            */
            d = &Buff[m];
            c = atof(d);//把Buff数组中的数字字符串转换为浮点型数据
            m = j;
            Pushstack_num(N, c);//把数字压入数据栈
        }

        else //如果该字符是运算符,则执行如下代码
        {
            /*遇到开始标志的‘#’,直接进入算符栈*/
            if (str[i] == '#' && i == 0)//str[i]=='#'&&i==0   是为了区分‘#’是算式的开始标志,还是算式的结束标志
            {
                k = str[i];
                Pushstack_sym(S, k);
            }
            /*遇到左括号‘(’字符,直接进入算符栈*/
            else if (str[i] == '(')
            {
                k = str[i];
                Pushstack_sym(S, k);
            }
            /*
            ‘+’‘-’属于同等级运算符,不需要比较
            */
            else if (str[i] == '+' || str[i] == '-')//如果遇到字符是‘+’或者‘-’,在算符栈中若只含有‘#’‘(’,那么该运算字符直接入算符栈
            {

                k = str[i];
                n = Popstack_sym(S);
                if (n == '#' || n == '(')
                {
                    Pushstack_sym(S, n);
                    Pushstack_sym(S, k);
                }
                else
                {
                    while (n == '*' || n == '/' || n == '+' || n == '-')//如果栈顶运算符是‘*’'-''+'‘/’,那么就先取出数据栈中的两个数字,调用运算函数,进行计算
                    {
                        /*取出数据栈中两个数字,调用运算函数,传入a,b参数,进行计算,再将返回值放入数据栈*/
                        a = Popstack_num(N);
                        b = Popstack_num(N);  
                        f = calculate(a, b, n);
                        Pushstack_num(N, f);
                        n = Popstack_sym(S);//再次取出算符栈栈顶字符,判断n是否为 ‘+’‘-’‘*’‘/’
                    }
                    /*如果取出的是字符‘#’或‘(’时,跳出while循环,并把‘#’或‘(’字符再压入算符栈,同时字符k也压入算符栈*/
                    Pushstack_sym(S, n);
                    Pushstack_sym(S, k);
                }

            }
            else if (str[i] == '*' || str[i] == '/')//如果检测到字符为‘*’‘/’时,执行以下操作
            {
                k = str[i];
                n = Popstack_sym(S);//取出算符栈中的一个字符
                if (n == '#' || n == '+' || n == '-' || n == '(')//对该字符进行判断:‘*’‘/’的优先级大于‘#’‘+’‘-’‘(’,直接压入算符栈
                {
                    Pushstack_sym(S, n);
                    Pushstack_sym(S, k);
                }
                else//
                {
                    while (n == '*' || n == '/')//若是遇到同等级的运算符则需要将进行以下操作
                    {
                        /*取出数据栈中两个数字,调用运算函数,传入a,b参数,进行计算,再将返回值放入数据栈*/
                        a = Popstack_num(N);
                        b = Popstack_num(N);
                        f = calculate(a, b, n);
                        Pushstack_num(N, f);
                        
                        n = Popstack_sym(S);//再次取出算符栈中的栈顶字符,判断是否为’*‘’/‘字符,如果不是跳出循环,如果是继续循环
                    }
                    /*如果取出的是字符‘#’或‘(’时,跳出while循环,并把‘#’或‘(’字符再压入算符栈,字符k也压入算符栈*/
                    Pushstack_sym(S, n);
                    Pushstack_sym(S, k);
                }
            }
            /*遇到‘)’字符时,不需要将其压入算符栈,此时需将算符栈中的运算字符取出,调用运算函数,直到取到‘(’字符为止。*/
            else if (str[i] == ')')
            {
                int s = 0;
                n = Popstack_sym(S);
                while (n != '(')
                {
                    a = Popstack_num(N);
                    b = Popstack_num(N);
                    f = calculate(a, b, n);
                    Pushstack_num(N, f);
                    n = Popstack_sym(S);//如果在这一步取出的栈顶字符是‘(’,除了会退出循环以外,‘(’字符也不需要在进入算符栈中了,直接丢即可
                }

            }
            /*
             在把输入的所有字符进行分栈压入之后,当检测到‘#’结束字符之后时,是不需要在把‘#’字符压入算符栈中,
             这时需要退出栈分配阶段,取数据栈和算符栈中元素进行最后的计算,直至遇到算符栈栈底中的‘#’字符时,输出数据栈中的计算结果结果
            */
            else if (str[i] == '#' && i != 0)
            {
                n = Popstack_sym(S);
                while (n != '#')
                {
                    a = Popstack_num(N);
                    b = Popstack_num(N);
                    f = calculate(a, b, n);
                    Pushstack_num(N, f);
                    n = Popstack_sym(S);
                }
                value = Popstack_num(N);
                printf("该算式的计算结果为:               %0.2lf\n", value);

            }

            i++;
        }

    }


}

接下来是运算函数 calculate() :

/*运算函数*/
double calculate(double a, double b, char n)
{
    switch (n)
    {
    case '+':   return  b + a;
    case '-':   return  b - a;
    case '*':   return  b * a;
    case '/':   return  b / a;
    default:   exit(0);//如果接收的不是:‘+’‘-’‘*’‘/’时,直接退出程序,当然这种情况是不存在的!
    }
}

好了好了,终于一段一段的说完了,
那么是时候供上计算器的完整代码了:

/*静态栈式计算器*/
#include<stdio.h>
#include<stdlib.h>

#define maxsize 30

/*数据栈*/
typedef struct Stack_F
{
    double data[maxsize];
    int top; //栈顶指针
}num;   //number 数字 

/*算符栈*/
typedef struct Stack_C
{
    char data[maxsize];
    int top; //栈顶指针
}sym;   //symbol 符号

/*数据栈初始化*/
void Initstack_num(num& N)
{
    N.top = 0;
}

/*算符栈初始化*/
void Initstack_sym(sym& S)
{
    S.top = 0;
}

/*数据栈压栈*/
void Pushstack_num(num& N, double e)
{
    N.data[N.top] = e;
    N.top++;
}

/*算符栈压栈*/
void Pushstack_sym(sym& S, char e)
{
    S.data[S.top] = e;
    S.top++;
}

/*数据栈出栈*/
double Popstack_num(num& N)
{
    N.top--;
    return N.data[N.top];
}

/*算符栈出栈*/
char Popstack_sym(sym& S)
{
    S.top--;
    return S.data[S.top];
}

/*分类函数*/
void sort(num& N, sym& S, char str[])
{
    void Pushstack_num(num & N, double e);
    void Pushstack_sym(sym & S, char e);
    double Popstack_num(num & N);
    char Popstack_sym(sym & S);
    double calculate(double a, double b, char n);
    int i = 0, j = 0,m=0;
    double a, b;//接收数据栈返回值
    double c;//暂时存储数字字符转换成浮点型数值
    char* d;//声明d指针,是为了后续调用atof函数:   double    __cdecl atof   (_In_z_ char const* _String);
    double f;//接收运算函数的返回值,将其压入数据栈
    char k;//接收str数组中一个临时字符
    char n;//接收算符栈的返回值
    double value;//最终算式的结果值
    char Buff[maxsize];//预存数字字符串
    while (str[i] != '\0')//判断第i个字符是否为截至字符,如果是,跳出while循环,如果不是,进入while循环
    {
        if (str[i] >= '0' && str[i] <= '9')//如果该字符为数字字符,则执行如下代码
        {

            while (str[i] >= '0' && str[i] <= '9')//如果该字符为数字字符,则进入while循环
            {
                Buff[j] = str[i];//把str[i]字符赋值给Buff[maxsize]数组
                j++;
                i++;
            }
            /*
            当上面的while循环退出之后,Buff数组中是存有数字字符的,用d指针指向数组下标为m时的位置,调用atof函数,
            从m位置(m初始化为0)开始,直到遇见数组中的‘\0’字符时,把这之间的数字字符转换成数字
            */
            d = &Buff[m];
            c = atof(d);//把Buff数组中的数字字符串转换为浮点型数据
            m=j;
            Pushstack_num(N, c);//把数字压入数据栈
        }

        else //如果该字符是运算符,则执行如下代码
        {
            /*遇到开始标志的‘#’,直接进入算符栈*/
            if (str[i] == '#' && i == 0)//str[i]=='#'&&i==0   是为了区分‘#’是算式的开始标志,还是算式的结束标志
            {
                k = str[i];
                Pushstack_sym(S, k);
            }
            /*遇到左括号‘(’字符,直接进入算符栈*/
            else if (str[i] == '(')
            {
                k = str[i];
                Pushstack_sym(S, k);
            }
            /*
            ‘+’‘-’属于同等级运算符,不需要比较
            */
            else if (str[i] == '+' || str[i] == '-')//如果遇到字符是‘+’或者‘-’,在算符栈中若只含有‘#’‘(’,那么该运算字符直接入算符栈
            {

                k = str[i];
                n = Popstack_sym(S);
                if (n == '#' || n == '(')
                {
                    Pushstack_sym(S, n);
                    Pushstack_sym(S, k);
                }
                else
                {
                    while (n == '*' || n == '/' || n == '+' || n == '-')//如果栈顶运算符是‘*’'-''+'‘/’,那么就先取出数据栈中的两个数字,调用运算函数,进行计算
                    {
                        /*取出数据栈中两个数字,调用运算函数,传入a,b参数,进行计算,再将返回值放入数据栈*/
                        a = Popstack_num(N);
                        b = Popstack_num(N);  
                        f = calculate(a, b, n);
                        Pushstack_num(N, f);
                        n = Popstack_sym(S);//再次取出算符栈栈顶字符,判断n是否为 ‘+’‘-’‘*’‘/’
                    }
                    /*如果取出的是字符‘#’或‘(’时,跳出while循环,并把‘#’或‘(’字符再压入算符栈,同时字符k也压入算符栈*/
                    Pushstack_sym(S, n);
                    Pushstack_sym(S, k);
                }

            }
            else if (str[i] == '*' || str[i] == '/')//如果检测到字符为‘*’‘/’时,执行以下操作
            {
                k = str[i];
                n = Popstack_sym(S);//取出算符栈中的一个字符
                if (n == '#' || n == '+' || n == '-' || n == '(')//对该字符进行判断:‘*’‘/’的优先级大于‘#’‘+’‘-’‘(’,直接压入算符栈
                {
                    Pushstack_sym(S, n);
                    Pushstack_sym(S, k);
                }
                else//
                {
                    while (n == '*' || n == '/')//若是遇到同等级的运算符则需要将进行以下操作
                    {
                        /*取出数据栈中两个数字,调用运算函数,传入a,b参数,进行计算,再将返回值放入数据栈*/
                        a = Popstack_num(N);
                        b = Popstack_num(N);
                        f = calculate(a, b, n);
                        Pushstack_num(N, f);
                        
                        n = Popstack_sym(S);//再次取出算符栈中的栈顶字符,判断是否为’*‘’/‘字符,如果不是跳出循环,如果是继续循环
                    }
                    /*如果取出的是字符‘#’或‘(’时,跳出while循环,并把‘#’或‘(’字符再压入算符栈,字符k也压入算符栈*/
                    Pushstack_sym(S, n);
                    Pushstack_sym(S, k);
                }
            }
            /*遇到‘)’字符时,不需要将其压入算符栈,此时需将算符栈中的运算字符取出,调用运算函数,直到取到‘(’字符为止。*/
            else if (str[i] == ')')
            {
                int s = 0;
                n = Popstack_sym(S);
                while (n != '(')
                {
                    a = Popstack_num(N);
                    b = Popstack_num(N);
                    f = calculate(a, b, n);
                    Pushstack_num(N, f);
                    n = Popstack_sym(S);//如果在这一步取出的栈顶字符是‘(’,除了会退出循环以外,‘(’字符也不需要在进入算符栈中了,直接丢即可
                }

            }
            /*
             在把输入的所有字符进行分栈压入之后,当检测到‘#’结束字符之后时,是不需要在把‘#’字符压入算符栈中,
             这时需要退出栈分配阶段,取数据栈和算符栈中元素进行最后的计算,直至遇到算符栈栈底中的‘#’字符时,输出数据栈中的计算结果结果
            */
            else if (str[i] == '#' && i != 0)
            {
                n = Popstack_sym(S);
                while (n != '#')
                {
                    a = Popstack_num(N);
                    b = Popstack_num(N);
                    f = calculate(a, b, n);
                    Pushstack_num(N, f);
                    n = Popstack_sym(S);
                }
                value = Popstack_num(N);
                printf("该算式的计算结果为:               %0.2lf\n", value);

            }

            i++;
        }

    }


}

/*运算函数*/
double calculate(double a, double b, char n)
{
    switch (n)
    {
    case '+':   return  b + a;
    case '-':   return  b - a;
    case '*':   return  b * a;
    case '/':   return  b / a;
    default:   exit(0);//如果接收的不是:‘+’‘-’‘*’‘/’时,直接退出程序,当然这种情况是不存在的!
    }
}

/*主函数*/
int main()
{
    void sort(num & N, sym & S, char str[]);
    void Initstack_num(num & N);
    void Initstack_sym(sym & S);
    char str[maxsize];//初始化字符串大小
    num N;
    sym S;
    printf("***********************************************************************************************************************\n");
    printf("*****************************************************栈式简单计算器****************************************************\n");
    printf("\n提示:输入的算式以 ‘#’ 符号为开始符,以  ‘#’ 符号为结束符,在算式中可以包含的运算有‘+’‘-’‘*’‘/’‘(’‘)’,输入完毕之后,直接回车即可\n ");
    printf("\n\n注意:运算符必须在英式状态下输入!!!");
    printf("\n\n\n输入算式示例:       #1+2#   ");
    printf("\n\n请输入所需要计算的算式:           ");
    gets_s(str);//输入字符串
    Initstack_num(N);
    Initstack_sym(S);
    sort(N, S, str);
    return 0;
}

另外呢,在程序中用到了 <stdlib.h>中的 atof函数。在此呢,本猿再分享给各位猿猿(媛媛)一个东东:(C语言函数大全)
链接:https://pan.baidu.com/s/1op0-NiSWCuJ3OaCkC1U9Wg
提取码:ogqd

本猿C位出道。刚学习算法不久,领悟也多有不足,若是有更好思路的媛友(猿友),还请不吝赐教。。。

原文地址:https://www.cnblogs.com/shuqingsong/p/12640693.html