#刚学习完栈,自己编写了一个计算器,思路都是自己想的,目前实现的功能是可以进行加减乘除,括号以及小数点结合的运算,但是负数还没有实现,可能有些未知的bug,欢迎各位大佬指点。
前置的基本函数的定义。
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include<math.h>
//#include<stack>
// 定义栈结构
#define MAX_SIZE 1000
typedef struct {
double data[MAX_SIZE];
int top;
} Stack;
// 初始化栈
void initStack(Stack* s)
{
s->top = -1;//空
}
// 判断栈是否为空
int isEmpty(Stack* s)
{
return (s->top == -1);
}
// 入栈
void push(Stack* s, double value)
{
s->top++;//将栈顶指针往上移动,进行输入。先移动再输入。。。
s->data[s->top] = value;
}
// 出栈
double pop(Stack* s)
{
return s->data[s->top--];
}
// 获取栈顶元素
double top(Stack* s)
{
return s->data[s->top];
}
// 判断是否为操作符
int isOperator(char c)
{
return c == '+' || c == '-' || c == '*' || c == '/'|| c == '(' || c == ')' || c == '.';
}
// 判断是否为数字字符
int isnumber(char c)
{
return c >= '0' && c <= '9';
}
// 进行运算
double calculate(double a, double b, char op)
{
switch (op)
{
case '+':
return a + b;
case '-':
return a - b;
case '*':
return a * b;
case '/':
return a / b;
default:
return 0;
}
}
// 判断运算符的优先级
int precedence(char op) //还没有考虑有人将中文符号输入的情况。。。
{
if (op == '(' || op == ')')
{
return 3;
}
else if (op == '*' || op == '/')
{
return 2;
}
else if (op == '+' || op == '-')
{
return 1;
}
}
——————————————————————————————————————————
然后是主要的运算函数
double endculated(char* expression)
{
Stack numStack;
initStack(&numStack);//初始化数字栈
Stack opStack;
initStack(&opStack);//初始化符号栈
int i = 0;
while (expression[i] != '\0')
{
if (expression[i] == ' ') //判断是否为空格
{
i++;
continue;
}//消除空格的影响,但是运算中间的空格不能消除??
else if (isnumber(expression[i])) //判断是否为 数字字符
{
double num = 0;
while (isnumber(expression[i]))
{
num = num*10+(expression[i] - '0');//将字符串中对应字符转化为整形形式。
i++;
}
push(&numStack, num);//将转化后的整型数字入栈
}
else if (isOperator(expression[i])) //判断是否为操作符
{ //如果栈不为空并且即将进入的操作符优先级小于栈内的元素,我们就要把原来栈内的符号处理。
if (expression[i] == '.')
{
double c = 0.1; double num = 0.0; i++;
while (isnumber(expression[i]))
{
num = num + (expression[i] - '0') * c;
c*=0.1; i++;
}
double last = pop(&numStack);
double newnumber = last + num;
push(&numStack, newnumber);
}
else if (expression[i] == '(')
{
push(&opStack, '(');
i++;
}
else if (expression[i] == ')')
{
while (!isEmpty(&opStack) && top(&opStack) != '(')
{
double b = pop(&numStack);
double a = pop(&numStack);
char op = pop(&opStack);
double result = calculate(a, b, op);
push(&numStack, result);
}
pop(&opStack); // 弹出左括号
i++;
}
else
{
while (!isEmpty(&opStack) && (precedence(expression[i]) <= precedence(top(&opStack))) && ((top(&opStack) != '(')))
{
double b = pop(&numStack);//取出两个数字,以及一个操作符,先进行一次运算。
double a = pop(&numStack);
char op = pop(&opStack);
push(&numStack, calculate(a, b, op));//再将运算结果进入数栈等待下一次运算
}
push(&opStack, expression[i]);//新的操作符入栈
i++;
}
}
}
//最终计算,返回计算结果。
while (!isEmpty(&opStack)) //如果操作符栈不为空,就接着计算
{
double b = pop(&numStack);//计算过程仍为取出两个数字加一个操作符
double a = pop(&numStack);
char op = pop(&opStack);
push(&numStack, calculate(a, b, op));//再将一次运算结果返回数栈。
}
return top(&numStack);
}
普通运算的原理大致为,输入运算式的字符串,然后用while循环进行对字符串的遍历,当判断为数字字符或运算字符时,进入相应的栈内。
当运算时:例如 "5 - 1 * 3" 这个运算式,数字5先进数字栈,再是" - " 进入符号栈,然后是数字1进入数字栈,再然后当" * " 号进栈时,需要比较" * " 号与符号栈内的符号的优先级,先算乘除后算加减,所以乘除的优先级是大于加减的,所以这里" * "号可以进栈,若这里进栈的符号为" + " 或 " - "号,则符号无法进栈,因为优先级相同,要将栈内的元素先进行一次运算才能使新符号进栈。
单次运算逻辑:前面说到将数字与符号分为两个栈,当我们进行单次运算时,只需要在数字栈中取出两个元素,再在符号栈中取出一个元素,三个元素进行运算,最后将结果再放回数字栈中,等待下一次的取用。
以上关于普通加减乘除的运算逻辑————————————————————————
关于使用括号的逻辑,一开始我的设想为直接将左右括号的优先级设置为最高,因为现实计算中也确实如此,但是实际运算中会发现如果这样设置,在左括号进栈后,加减乘除运算符因为优先级低于左括号,均无法进栈。 所以我直接运用自身逻辑在运算中特殊处理。 当遇到左括号时,直接进栈,中间运算符号正常进栈,在碰到右括号时进行一次左右括号的运算(用while循环,取出栈内两个数字以及符号,将运算结果重新丢入数字栈中,停止条件设置为遇到左括号为止,这样可以解决括号内的多重运算)
而对于小数的实现,因为整个运算基础是对字符串的遍历,若有小数的输入一定会遍历到小数点,我将小数点归到符号一类,当没有检测到小数点时,数字以及符号正常进栈,当检测到为符号时,且为小数点时候,因为前面数字是正常进栈的所以此时数字栈内已经存放了小数点前的数字,例如"25.33",在检测到小数点前,"25"就已经正常进入数字栈内,然后再通过乘0.1将小数部分求出,取出数字栈顶元素(即"25")相加,即可实现小数进栈。
——————————————————————————————————————————
最后是完整代码,欢迎各位批评指正!!
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include<math.h>
//#include<stack>
// 定义栈结构
#define MAX_SIZE 1000
typedef struct {
double data[MAX_SIZE];
int top;
} Stack;
// 初始化栈
void initStack(Stack* s)
{
s->top = -1;//空
}
// 判断栈是否为空
int isEmpty(Stack* s)
{
return (s->top == -1);
}
// 入栈
void push(Stack* s, double value)
{
s->top++;//将栈顶指针往上移动,进行输入。先移动再输入。。。
s->data[s->top] = value;
}
// 出栈
double pop(Stack* s)
{
return s->data[s->top--];
}
// 获取栈顶元素
double top(Stack* s)
{
return s->data[s->top];
}
// 判断是否为操作符
int isOperator(char c)
{
return c == '+' || c == '-' || c == '*' || c == '/'|| c == '(' || c == ')' || c == '.';
}
// 判断是否为数字字符
int isnumber(char c)
{
return c >= '0' && c <= '9';
}
// 进行运算
double calculate(double a, double b, char op)
{
switch (op)
{
case '+':
return a + b;
case '-':
return a - b;
case '*':
return a * b;
case '/':
return a / b;
default:
return 0;
}
}
// 判断运算符的优先级
int precedence(char op) //还没有考虑有人将中文符号输入的情况。。。
{
if (op == '(' || op == ')')
{
return 3;
}
else if (op == '*' || op == '/')
{
return 2;
}
else if (op == '+' || op == '-')
{
return 1;
}
//else
//{
// return 0; // 其他情况,如括号等
//}
}
// 计算表达式
double endculated(char* expression)
{
Stack numStack;
initStack(&numStack);//初始化数字栈
Stack opStack;
initStack(&opStack);//初始化符号栈
int i = 0;
while (expression[i] != '\0')
{
if (expression[i] == ' ') //判断是否为空格
{
i++;
continue;
}//消除空格的影响,但是运算中间的空格不能消除??
else if (isnumber(expression[i])) //判断是否为 数字字符
{
double num = 0;
while (isnumber(expression[i]))
{
num = num*10+(expression[i] - '0');//将字符串中对应字符转化为整形形式。
i++;
}
push(&numStack, num);//将转化后的整型数字入栈
}
else if (isOperator(expression[i])) //判断是否为操作符
{ //如果栈不为空并且即将进入的操作符优先级小于栈内的元素,我们就要把原来栈内的符号处理。
if (expression[i] == '.')
{
double c = 0.1; double num = 0.0; i++;
while (isnumber(expression[i]))
{
num = num + (expression[i] - '0') * c;
c*=0.1; i++;
}
double last = pop(&numStack);
double newnumber = last + num;
push(&numStack, newnumber);
}
else if (expression[i] == '(')
{
push(&opStack, '(');
i++;
}
else if (expression[i] == ')')
{
while (!isEmpty(&opStack) && top(&opStack) != '(')
{
double b = pop(&numStack);
double a = pop(&numStack);
char op = pop(&opStack);
double result = calculate(a, b, op);
push(&numStack, result);
}
pop(&opStack); // 弹出左括号
i++;
}
else
{
while (!isEmpty(&opStack) && (precedence(expression[i]) <= precedence(top(&opStack))) && ((top(&opStack) != '(')))
{
double b = pop(&numStack);//取出两个数字,以及一个操作符,先进行一次运算。
double a = pop(&numStack);
char op = pop(&opStack);
push(&numStack, calculate(a, b, op));//再将运算结果进入数栈等待下一次运算
}
push(&opStack, expression[i]);//新的操作符入栈
i++;
}
}
}
//最终计算,返回计算结果。
while (!isEmpty(&opStack)) //如果操作符栈不为空,就接着计算
{
double b = pop(&numStack);//计算过程仍为取出两个数字加一个操作符
double a = pop(&numStack);
char op = pop(&opStack);
push(&numStack, calculate(a, b, op));//再将一次运算结果返回数栈。
}
return top(&numStack);
}
int main() {
char expression[100];
while (1) {
printf("请输入表达式(不含空格):");
scanf("%s", expression);
double result = endculated(expression);
printf("计算结果为:%f\n", result);
}
return 0;
}
//整个制作的思路与过程:
//初始化操作数栈和操作符栈:首先初始化两个栈,一个用于存储操作数,一个用于存储操作符。
//遍历表达式:遍历用户输入的表达式,逐个处理数字和运算符。
//处理数字:当遇到数字时,将其转换为整数并入栈。
//处理运算符:当遇到运算符时,根据运算符的优先级决定是否先计算栈顶的操作符。
//计算结果:最终通过出栈和计算函数,得到最终的计算结果。
//输出结果