一、实验要求
1.描述
这里限定的表达式求值问题是:用户输入一个包含“+”、“-”、“*”、“/”、正整数和圆括号的合法数学表达式,计算该表达式的运算结果
2.输入
表达式字符串
3.输出
表达式值
4.样例输入
15
5.提示
1)运算符优先级关系
|
+ |
- |
× |
/ |
( |
) |
# |
+ |
> |
> |
< |
< |
< |
> |
> |
- |
> |
> |
< |
< |
< |
> |
> |
× |
> |
> |
> |
> |
< |
> |
> |
/ |
> |
> |
> |
> |
< |
> |
> |
( |
< |
< |
< |
< |
< |
= |
|
) |
> |
> |
> |
> |
|
> |
> |
# |
< |
< |
< |
< |
< |
|
= |
2)涉及的基本操作(函数)
函数名 |
功能 |
函数名 |
功能 |
---|---|---|---|
InitStack() |
初始化栈 |
Push() |
入栈 |
GetTop() |
获栈顶元素 |
Precede() |
优先级比较 |
Pop() |
出栈 |
Operate() |
运算 |
3)解题思想实现算法描述
OperandType EvaluateExpression()
{ //算术表达式求值的算符优先算法。设OPTR和OPND分别为运算符栈和运算数栈,OP为运算符的集合。
InitStack(OPTR);Push(OPTR,'#');
InitStack(OPND);c=getchar();
while(c!='#'||GetTop(OPTR)!='#')
{
if(!In(c,OP))
{
Push((OPND,c);
c=getchar();
}
//不是运算符则进栈
else
switch(Precede(GetTop(OPTR), c)
{
case '<'://栈顶元素优先权低
Push(OPTR,c); c=getchar(); break;
case '=':// 脱括号并接收下一个字符
Pop(OPTR, x); c=getchar(); break;
case ‘>’://出栈,并将运算结果入栈
Pop(OPTR,theta);
Pop(OPND,b); Pop(OPND,a);
Push(OPND,Operate(a,theta,b));
}//swith
}//while
return GetTop(OPND);
}//EvaluateExpression
二、需求分析
本实验通过C语言来实现数据结构中的栈,以及通过使用栈这个数据结构完成1~9整数之间的加、减、乘、除四则混合运算的实验,并完成指定的测试操作。
- 从键盘输入:数字1-9、“(”左括号、“)”右括号、“+”加号、“-”减号、“*”乘号、“/”除号构成的表达式并以“#”号结尾,结果输出:形如:“结果=”+表达式的值。
- 本程序没有边界约束,违反输入规则的均会导致程序运行失败。
三、概要设计
1.抽象数据类型的定义
typedef struct{
SElemType *base; //在栈构造之前和销毁之后,base的初值为NULL
SElemType *top; //栈顶指针,设栈顶、栈底指针的目的是便于判断栈是否为空
int stacksize; //当前已分配的存储空间,以元素为单位.
}SqStack;//栈
2.主程序的流程图
3.本程序各功能模块之间均不互相调用,单独成块,由主函数一一调用。
四、详细设计
1.程序开始预编译部分如下:
//头文件
#include <stdio.h>
#include <stdlib.h>
// 函数结果状态代码
#define OVERFLOW -2
#define TRUE 1
#define FALSE 0
#define OK 1
#define ERROR 0
typedef int Status;
typedef char RElemtype;
typedef int DElemtype;
//内存空间数量级
#define STACK_INIT_SIZE 100
#define STACKINCREMENT 10
typedef struct {
RElemtype *base;
RElemtype *top;
int stacksize;
} SqStack1;//运算符栈类型定义
typedef struct {
DElemtype *base;
DElemtype *top;
int stacksize;
} SqStack2;//操作数栈类型定义
2.程序主要功能函数如下:
DElemtype EvaluateExpression();//计算表达式值
Status OPTR_InitStack(SqStack1 *S);//运算符栈的初始化
Status OPND_InitStack(SqStack2 *S);//操作数栈的初始化
RElemtype OPTR_GetTop(SqStack1 S);//取运算符栈的栈顶元素
DElemtype OPND_GetTop(SqStack2 S);//取操作数栈的栈顶元素
Status OPTR_Push(SqStack1 *S, RElemtype e);//压栈
Status OPND_Push(SqStack2 *S, DElemtype e);//压栈
RElemtype OPTR_Pop(SqStack1 *S, RElemtype *e);//出栈
DElemtype OPND_Pop(SqStack2 *S, DElemtype *e);//出栈
char Precede(char a, char b);//运算符优先级比较
DElemtype Operate(DElemtype d, char e, DElemtype f);//操作数d、f参加e运算的结果
extern int In(char c);//判断c是否是运算符
3.函数调用关系图
五、调试分析
- 在EvaluateExpression()表达式计算中,默认开始初始化OPTR运算符栈和OPND运算数栈,在运算符栈的栈底要默认有一个“#”号用于结束表达式的计算和字符输入。刚开始我没加,导致程序一直需要输入字符,无法结束,之后加上,解决。
- In(char c)运算符判断函数,刚开始写时忘记加入左右括号和“#”号的判断,没有对“#”号导致EvaluateExpression()函数中的while循环成死循环状态,最终使程序陷入死循环;没有判断括号,导致括号入错栈,造成表达式的计算的顺序和计算的数的位置出现问题,括号被做为数值参与到运算,同时导致运算数栈存在计算完成,元素仍有存留的现象,最终导致计算结果不正确。
- 如何将输出的char字符转成正确的int数值存入操作数栈中?解决办法:每次存入OPND的char字符减去字符‘0’的ASCII值从而得到正确的int数值,在存入其中。
- 比较两个运算符的优先级函数char Precede(char a, char b)思路解析:
运算符优先级比较表:
将运算符之间的优先级制作成一张表格,使用二维数组存放;
char pre[][7] = {
//将运算符之间的优先级制作成一张表格
{'>', '>', '<', '<', '<', '>', '>'},
{'>', '>', '<', '<', '<', '>', '>'},
{'>', '>', '>', '>', '<', '>', '>'},
{'>', '>', '>', '>', '<', '>', '>'},
{'<', '<', '<', '<', '<', '=', '0'},
{'>', '>', '>', '>', '0', '>', '>'},
{'<', '<', '<', '<', '<', '0', '='}
};
通过两个switch语句来定位需要判断符号的优先级所对应的数组的一维下标和二维下标,取出指定的元素,'>'表示a>b;'<'表示a<b;'0'表示不可能出现的比较;“(“=”)”表示左右括号相遇,括号内的运算已经完成, “#“=”#” 表达式求值完成。
switch (a) {
case '+':i = 0;break;
case '-':i = 1;break;
case '*':i = 2;break;
case '/':i = 3;break;
case '(':i = 4;break;
case ')':i = 5;break;
case '#':i = 6;break;
default:return OVERFLOW;
}
switch (b) {
case '+':j = 0;break;
case '-':j = 1;break;
case '*':j = 2;break;
case '/':j = 3;break;
case '(':j = 4;break;
case ')':j = 5;break;
case '#':j = 6;break;
default:return OVERFLOW;
}
六、测试数据与结果
1.开始界面
2.输入 指定格式的运算符表达式(因为格式有所要求,故不做边界测试)
如:2+3x(5+6-3x2)+4/(6-4)-3x3+1=11
结果如下:
3.输出结果格式:“结果=” +表达式的值
实验成功!
附录——源代码清单
fun.h
//头文件
#include <stdio.h>
#include <stdlib.h>
// 函数结果状态代码
#define OVERFLOW -2
#define TRUE 1
#define FALSE 0
#define OK 1
#define ERROR 0
typedef int Status;
typedef char RElemtype;
typedef int DElemtype;
//内存空间数量级
#define STACK_INIT_SIZE 100
#define STACKINCREMENT 10
typedef struct {
RElemtype *base;
RElemtype *top;
int stacksize;
} SqStack1;//运算符栈类型定义
typedef struct {
DElemtype *base;
DElemtype *top;
int stacksize;
} SqStack2;//操作数栈类型定义
extern DElemtype EvaluateExpression();//计算表达式值
extern Status OPTR_InitStack(SqStack1 *S);//运算符栈的初始化
extern Status OPND_InitStack(SqStack2 *S);//操作数栈的初始化
extern RElemtype OPTR_GetTop(SqStack1 S);//取运算符栈的栈顶元素
extern DElemtype OPND_GetTop(SqStack2 S);//取操作数栈的栈顶元素
extern Status OPTR_Push(SqStack1 *S, RElemtype e);//压栈
extern Status OPND_Push(SqStack2 *S, DElemtype e);//压栈
extern RElemtype OPTR_Pop(SqStack1 *S, RElemtype *e);//出栈
extern DElemtype OPND_Pop(SqStack2 *S, DElemtype *e);//出栈
extern char Precede(char a, char b);//运算符优先级比较
extern DElemtype Operate(DElemtype d, char e, DElemtype f);//操作数d、f参加e运算的结果
extern int In(char c);//判断c是否是运算符
main.c
#include "fun.h"
/**
* 默认栈底有一个结束符‘#’
* 操作符忘记判断#号 (号 )号 死了多次
* 操作数ascii值转int再运算 死了两次
*/
Status OPTR_InitStack(SqStack1 *S) {//运算符栈的初始化
//构造一个空栈S
S->base = (RElemtype *) malloc(STACK_INIT_SIZE * sizeof(RElemtype));
if (!S->base) return (OVERFLOW); //存储分配失败
S->top = S->base;
S->stacksize = STACK_INIT_SIZE;
return OK;
}
Status OPND_InitStack(SqStack2 *S) {//操作数栈的初始化
//构造一个空栈S
S->base = (DElemtype *) malloc(STACK_INIT_SIZE * sizeof(DElemtype));
if (!S->base) return (OVERFLOW); //存储分配失败
S->top = S->base;
S->stacksize = STACK_INIT_SIZE;
return OK;
}
RElemtype OPTR_GetTop(SqStack1 S) {//取运算符栈的栈顶元素
if (S.top == S.base) return ERROR;//若栈不空,则用e返回S的栈顶元素,并返回OK;否则返回ERROR
return *(S.top - 1); //栈顶指针不变
}
DElemtype OPND_GetTop(SqStack2 S) {//取操作数栈的栈顶元素
if (S.top == S.base) return ERROR;//若栈不空,则用e返回S的栈顶元素,并返回OK;否则返回ERROR
return *(S.top - 1); //栈顶指针不变
}
Status OPTR_Push(SqStack1 *S, RElemtype e) {//压栈
//插入元素e为新的栈顶元素
if (S->top - S->base >= S->stacksize) {//栈满,追加存储空间
S->base = (RElemtype *) realloc(S->base, (S->stacksize + STACKINCREMENT) * sizeof(RElemtype));
if (!S->base) return (OVERFLOW);
S->top = S->base + S->stacksize;
S->stacksize += STACKINCREMENT;
}
*S->top++ = e;
return OK;
}
Status OPND_Push(SqStack2 *S, DElemtype e) {//压栈
//插入元素e为新的栈顶元素
if (S->top - S->base >= S->stacksize) {//栈满,追加存储空间
S->base = (DElemtype *) realloc(S->base, (S->stacksize + STACKINCREMENT) * sizeof(DElemtype));
if (!S->base) return (OVERFLOW);
S->top = S->base + S->stacksize;
S->stacksize += STACKINCREMENT;
}
*S->top++ = e;
return OK;
}
RElemtype OPTR_Pop(SqStack1 *S, RElemtype *e) {//出栈
//若栈不空,则删除S的栈顶元素,用e返回其值,并返回OK;否则返回ERROR
if (S->top == S->base) return ERROR; //栈空
*e = *--S->top;
return *e;
}
DElemtype OPND_Pop(SqStack2 *S, DElemtype *e) {//出栈
//若栈不空,则删除S的栈顶元素,用e返回其值,并返回OK;否则返回ERROR
if (S->top == S->base) return ERROR; //栈空
*e = *--S->top;
return *e;
}
char Precede(char a, char b) {
/**
* 比较两个运算符的优先级;
* a,b中存放待比较的运算符;
* '>'表示a>b;
* '0'表示不可能出现的比较.
*/
int i, j;
char pre[][7] = {
//将运算符之间的优先级制作成一张表格
{'>', '>', '<', '<', '<', '>', '>'},
{'>', '>', '<', '<', '<', '>', '>'},
{'>', '>', '>', '>', '<', '>', '>'},
{'>', '>', '>', '>', '<', '>', '>'},
{'<', '<', '<', '<', '<', '=', '0'},
{'>', '>', '>', '>', '0', '>', '>'},
{'<', '<', '<', '<', '<', '0', '='}
};
switch (a) {
case '+':
i = 0;
break;
case '-':
i = 1;
break;
case '*':
i = 2;
break;
case '/':
i = 3;
break;
case '(':
i = 4;
break;
case ')':
i = 5;
break;
case '#':
i = 6;
break;
default:
return OVERFLOW;
}
switch (b) {
case '+':
j = 0;
break;
case '-':
j = 1;
break;
case '*':
j = 2;
break;
case '/':
j = 3;
break;
case '(':
j = 4;
break;
case ')':
j = 5;
break;
case '#':
j = 6;
break;
default:
return OVERFLOW;
}
return pre[i][j];
}
DElemtype Operate(DElemtype d, char e, DElemtype f) {//操作数d、f参加e运算的结果
DElemtype i = d, j = f, result;
switch (e) {
case '+':
result = i + j;
break;
case '-':
result = i - j;
break;
case '*':
result = i * j;
break;
case '/':
result = i / j;
break;
default://可能不会被使用
printf("出现非法运算符,程序终止!");
exit(0);
}
return result;
}
int In(char c) {//判断c是否是运算符
return c == '+' ||
c == '-' ||
c == '*' ||
c == '/' ||
c == '(' ||
c == ')' ||
c == '#' ?
TRUE : FALSE;
}
DElemtype EvaluateExpression() {//计算表达式值
/**
* 算术表达式求值的算符优先算法;
*设OPTR和OPND分别为运算符栈和运算数栈;
*/
SqStack1 OPTR;
SqStack2 OPND;
DElemtype a, b;
char flag;
char c, x, theta;
OPTR_InitStack(&OPTR);
OPTR_Push(&OPTR, '#');//默认栈底有一个结束符‘#’
OPND_InitStack(&OPND);
c = getchar();
while (c != '#' || OPTR_GetTop(OPTR) != '#') {
if (In(c) != TRUE) {//不是运算符则进操作数栈
OPND_Push(&OPND, c - '0');
c = getchar();
} else {
flag = Precede(OPTR_GetTop(OPTR), c);
switch (flag) {
case '<'://栈顶元素优先权低
OPTR_Push(&OPTR, c);
c = getchar();
break;
case '='://脱括号并接收下一字符
OPTR_Pop(&OPTR, &x);
c = getchar();
break;
case '>'://退栈并将运算结果入栈
OPTR_Pop(&OPTR, &theta);
OPND_Pop(&OPND, &b);
OPND_Pop(&OPND, &a);
OPND_Push(&OPND, Operate(a, theta, b));
break;
}//swith
}
}//while
return OPND_GetTop(OPND);
}//EvaluateExpression
int main() {
DElemtype c;
printf("请输入一个算数表达式(操作数为1~9的整数)并以“#”号结束:");
c = EvaluateExpression();
printf("结果=%d\n", c);
}