最近刚刚学完栈的相关结构,想着写一个小项目(题目的#号没有使用)
设计算法:1小时;代码实现:2小时;调试bug:12小时左右;
设计的算法思想:首先通过一个字符串数组c将操作者的式子全部放进去,之后将字符数组c的全部字符转入栈S1,由于式子中还有括号,所以要有一个式子括号检测是否匹配的问题,检测完毕后,提取栈S1中一个匹配括号的式子放入栈S2,再由一个整型数组a存放所有S2的内容(式子可能存在二位数以上所以要有一个合并数字字符的操作转入S3),通过安排好运算符的优先级就可以在整型数组a里面实现运算了,运算完毕最后的值放在a[0]的位置在传给原本的栈S1。式子内的括号全部运算完都放至栈S1,栈S1就有了完整的式子,之后将栈S1的全部数据转入一个整型数组进行运算(和括号内运算的代码类似)。
代码的内容不好做细分所以就没有一一化区块来展示了,下方是完整的代码:
使用的环境是vs2019;源文件后缀是.cpp
#define _CRT_SECURE_NO_WARNINGS//栈
#include <stdio.h>
#include <malloc.h>
#include<string>
//全局定义特殊变量
#define ok 1
#define OVERFLOW 0
#define MAX 30
//转定义区域
typedef char SElemType;
typedef int status;
typedef struct//定义栈
{
SElemType* top;
SElemType* base;
int Stacksize;
}Sqstack;
status InitStack(Sqstack& S)//栈的初始化函数//功能函数
{
S.base = (SElemType*)malloc(sizeof(SElemType[MAX]));
if (!S.base)
{
return OVERFLOW;
}
S.top = S.base;
S.Stacksize = MAX;
return ok;
}
status check(Sqstack& S, char c[50])//检查括号是否匹配//功能函数
{
int i, count;
for (i = 0; c[i] != '\0'; i++)//只录入括号
{
if (c[i] == '(')
{
*S.top++ = c[i];
}
if (c[i] == ')')
{
S.top--;
}
}
if (S.top == S.base)
{
count = 1;
}
else
{
count = 0;
}
return count;
}
status StackLength(Sqstack S)//输出栈的长度//功能函数
{
int i = 0;
SElemType* p;
p = S.top;
while (p != S.base)
{
--p;
i++;
}
return i;
}
status Clearstack(Sqstack& S)//清空栈
{
if (S.top = S.base)
{
return 1;
}
while (S.top != S.base)
{
--S.top;
}
return 1;
//printf("栈已清空!");
}
int main()
{
int count;
Sqstack S1, S2, S3;
InitStack(S1);
InitStack(S2);
InitStack(S3);
char c[50];//输入存放的式子
int a[50];//进行数字运算
int i, j, e, a1 = 0, F, x, y, len = 0, lens, zhi, mk = 0;
printf("欢迎使用表达式计算小程序:\n");
printf("请输入要计算的表达式:(等回车表示输入结束)");
gets_s(c);//源文件后缀为.c就不用加下划线
count = check(S1, c);
if (count == 1)
{
for (i = 0; c[i] != '\0'; i++)
{
if (c[i] == ')')//计算括号匹配的式子
{
while (1)
{
if (*--S1.top == '(')
{
break;
}
*S2.top++ = *S1.top;//通过S2将数字摆正=数组从S2出来后式子的顺序符合我们运算时的顺序
}
lens = StackLength(S2);//求得长度来限制数组a的存放次数
--S2.top;
if (*S2.top >= '0' && *S2.top < '10')//整合所有的数字
{
mark:while (*S2.top >= '0' && *S2.top < '10')
{
*S3.top++ = *S2.top;
--S2.top;
len++;
}
for (F = 1, y = 0, x = 0; S3.top != S3.base; F = F * 10)//若表达式的数字存在二位数及以上的就要合并
{
x = *--S3.top - '0';//利用的是ascll码表的规则进行字符到整型的转换
y = y + x * F;
}
a[a1] = y;
a1++;
if (*S2.top == '*')//符号直接进入整型数组会自动转化为对应的ascll码值进行保存
{
a[a1] = *S2.top--;
a1++;
len++;
}
if (*S2.top == '/')
{
a[a1] = *S2.top--;
a1++;
len++;
}
if (*S2.top == '+')
{
a[a1] = *S2.top--;
a1++;
len++;
}
if (*S2.top == '-')
{
a[a1] = *S2.top--;
a1++;
len++;
}
if (len != lens)
{
goto mark;
}
//以上是将栈保存的式子数据保存至整型数组进行运算
}
a[a1] = '\0';
for (e = 0; a[e] != '\0'; e++)//乘法除法运算//运算时涉及到运算符的优先级
{
if (a[e] == '*' || a[e] == '/')
{
if (a[e] == '*')
{
zhi = a[e - 1] * a[e + 1];
}
if (a[e] == '/')
{
zhi = a[e - 1] / a[e + 1];
}
a[e] = '\0'; a[e + 1] = '\0';
a[e - 1] = zhi;
for (; a[e + 2] != '\0'; e += 2)
{
a[e] = a[e + 2];
a[e + 2] = '\0';
a[e + 1] = a[e + 3];
a[e + 3] = '\0';
mk++;
}
if (mk > 0)//由于上面涉及到数组的移位,之后重新判断式子的时候要考虑到之前的循环条件for (e = 0; a[e] != '\0'; e++)
{
e -= 1 + (2 * mk);//进行参数的调整变化
mk = 0;
}
}
}
for (e = 0; a[e] != '\0'; e++)//加法减法运算
{
if (a[e] == '+' || a[e] == '-')
{
if (a[e] == '+')
{
zhi = a[e - 1] + a[e + 1];
}
if (a[e] == '-')
{
zhi = a[e - 1] - a[e + 1];
}
a[e] = '\0'; a[e + 1] = '\0';
a[e - 1] = zhi;
for (; a[e + 2] != '\0'; e += 2)
{
a[e] = a[e + 2];
a[e + 2] = '\0';
a[e + 1] = a[e + 3];
a[e + 3] = '\0';
mk++;
}
if (mk > 0)
{
e -= 1 + (2 * mk);
mk = 0;
}
}
}
Clearstack(S2);
while (a[0] != 0)//将括号结果转为字符准备入栈S1
{
*S2.top++=(a[0] % 10)+'0';
a[0] /= 10;
}
while (S2.top!=S2.base)
{
*S1.top++ = *--S2.top;
}
}
*S1.top++ = c[i];
if (c[i] == ')')
{
--S1.top;//出掉栈里面多余的括号
}
a1 = 0;
len = 0;//以上这两行初始化非常重要,防止数组的越界问题
}
while (S1.top != S1.base)
{
*S2.top++ = *--S1.top;
}
//接下来的代码和之前括号里面的运算是一致的
lens = StackLength(S2);
--S2.top;
Clearstack(S3);
if (*S2.top >= '0' && *S2.top < '10')//整合所有的数字
{
mark1:while (*S2.top >= '0' && *S2.top < '10')
{
*S3.top++ = *S2.top;
--S2.top;
len++;
}
for (F = 1, y = 0, x = 0; S3.top != S3.base; F = F * 10)
{
x = *--S3.top - '0';
y = y + x * F;
}
a[a1] = y;
a1++;
if (*S2.top == '*')
{
a[a1] = *S2.top--;
a1++;
len++;
}
if (*S2.top == '/')
{
a[a1] = *S2.top--;
a1++;
len++;
}
if (*S2.top == '+')
{
a[a1] = *S2.top--;
a1++;
len++;
}
if (*S2.top == '-')
{
a[a1] = *S2.top--;
a1++;
len++;
}
if (len != lens)
{
goto mark1;
}
}
a[a1] = '\0';
for (e = 0; a[e] != '\0'; e++)//乘法除法运算
{
if (a[e] == '*' || a[e] == '/')
{
if (a[e] == '*')
{
zhi = a[e - 1] * a[e + 1];
}
if (a[e] == '/')
{
zhi = a[e - 1] / a[e + 1];
}
a[e] = '\0'; a[e + 1] = '\0';
a[e - 1] = zhi;
for (; a[e + 2] != '\0'; e += 2)
{
a[e] = a[e + 2];
a[e + 2] = '\0';
a[e + 1] = a[e + 3];
a[e + 3] = '\0';
mk++;
}
if (mk > 0)
{
e -= 1 + (2 * mk);
mk = 0;
}
}
}
for (e = 0; a[e] != '\0'; e++)//加法减法运算
{
if (a[e] == '+' || a[e] == '-')
{
if (a[e] == '+')
{
zhi = a[e - 1] + a[e + 1];
}
if (a[e] == '-')
{
zhi = a[e - 1] - a[e + 1];
}
a[e] = '\0'; a[e + 1] = '\0';
a[e - 1] = zhi;
for (; a[e + 2] != '\0'; e += 2)
{
a[e] = a[e + 2];
a[e + 2] = '\0';
a[e + 1] = a[e + 3];
a[e + 3] = '\0';
mk++;
}
if (mk > 0)
{
e -= 1 + (2 * mk);
mk = 0;
}
}
}
printf("式子的结果为:%d",a[0]);
}
else
{
printf("该式子不符合语法");
}
return 0;
}
这个代码最容易出现的就是:某某的内容访问失败-->这个情况讲得首先考虑数组越界!注意做好数据元素的初始化(当初在这个地方一直调试不出来,花了好多时间,这个情况编译器是不会报错的,运行代码时就不行了);
实现的技术要点:
1.整型转字符,字符转整型(通过ascll码表来做的)
2.括号的检测(通过一个栈来实现的,栈空就是括号匹配)
3.数字的合并(这个花了不少功夫)
4.栈与数组的数据传输
5.“笑着”调试代码(作为初学者有bug很正常,要耐下心来不要急躁)
实施效果:
注意:不能出现:数字后面直接加(),如果要乘以括号里面的式子要:数字*();
目前缺陷:
1.算数的精度不够(由于是整型运算,一涉及到除法的运算就会有偏差,因为只取整)
2.只支持+ 、 - 、* 、/ 的运算
3.可能还有未知的bug(还望大佬们指出)