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#include #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; }
另外呢,在程序中用到了
链接:https://pan.baidu.com/s/1op0-NiSWCuJ3OaCkC1U9Wg
提取码:ogqd
本猿C位出道。刚学习算法不久,领悟也多有不足,若是有更好思路的媛友(猿友),还请不吝赐教。。。