最近有些无聊,整理了以前的代码,添加了取模以及幂运算,之前的代码链接
C+栈实现中缀转前、后缀并计算(四则计算器)+源码
资源 https://download.csdn.net/download/Ijerome/14839282
主要功能
- 支持浮点计算
- 支持 加减乘除 取模 幂运算
- 支持括号以及括号嵌套
- 较宽松的数据格式,比较符合书写规范 (例如-5+(-5))
- 返回多种错误类型
效果截图
正常输入(取模操作按照整形来算)
异常输入
代码涉及:栈,结构体,联合体,链表
主要分为几个文件
栈文件(处理栈),链表文件(处理链表以及计算和转换)
1.1 头文件 errorCode.h 定义异常类型
#ifndef _ERROR_CODE_H_
#define _ERROR_CODE_H_
// 用于定义多种返回值
#ifndef returnCode
#define returnCode int
#define TRUE_CODE (1) //正确返回
#define ERROR_CHAR (-1) //输入表达式存在不合法的符号
#define ERROR_SUB_ZERO (-2) //分母能不为0
#define ERROR_NUM_DOUBLE (-3)//运算符重复,缺少操作数
#define ERROR_POINT_DOUBLE (-4) //小数点重复
#define ERROR_BRACKET (-5) //括号不匹配
#define ERROR_MOD_ZERO (-6) //模数不能为0
#define ERROR_MALLOIC (-7) //内存不足
#define ERROR_POINT_NULL (-8) //空指针错误
#define ERROR_OTHER (-9) //其他错误
#endif
#endif // !_ERROR_CODE_H_
2.1 头文件 LStack.h 定义栈操作
#ifndef _LSTACK_H_
#define _LSTACK_H_
/************************************************************
Copyright (C), 1988-1999
FileName: LStack.h 2021.01.02
Author:hzp Version :1.0
Description: //链表存用于存放操作符或者操作数
Version: // v1.0
History: // 历史修改记录
<author> <time> <version > <desc>
***********************************************************/
#include "stdio.h"
#include "stdlib.h"
#include "errorCode.h"
//定义运算符标注
#define ADD (0)//加
#define SUB (1)//减
#define MUL (2)//乘
#define DIV (3)//除
#define MOD (4) //取模
#define POW (5) //幂
#define L_BRACKET (6)//左括号
#define R_BRACKET (7)//右括号
#define OPREATOR (1) //操作符类型
#define NUMBER (2) //操作数类型
//#define ADD_PRIORITY (1) //加
//#define SUB_PRIORITY (1) //减
//#define MUL_PRIORITY (2) //乘
//#define DIV_PRIORITY (2) //除
//#define MOD_PRIORITY (2) //取模
//#define POW_PRIORITY (3) //幂
//#define L_BRACKET_PRIORITY (4) //左括号
//#define R_BRACKET_PRIORITY (4) //右括号
typedef int operatorKind; //运算符类型数据
typedef double numberData; //操作数类型数据
typedef char elementKind;//标志元素类型
//联合体类型数据,存放操作符或者操作数
typedef union {
numberData num;
operatorKind kind;
}elementData;
//栈节点
typedef struct lNode *pNode;
typedef struct lNode {
elementData data; //数据
elementKind elementKind; //类型(是操作数还是运算符)
pNode next;
}lNode;
typedef struct {
pNode top; //栈顶
int count; //元素数量
}linkStack;
//初始化链栈
void initStack(linkStack *lStack);
//入栈
returnCode pushStack(linkStack *lStack , pNode pushNode);
//出栈
void popStack(linkStack *lStack, pNode popNode);
//查看栈顶元素(不出栈)
void readStack(linkStack *lStack, pNode popNode);
//释放栈
void freeStack(linkStack *lStack);
#endif
2.2 源文件 LStack.c
#include"LStack.h"
#include <stdlib.h>
/************************************************************
Copyright (C), 1988-1999
FileName: LStack.c 2021.01.02
Author:hzp Version :1.0
Description: //链表实现栈
Version: // v1.0
History: // 历史修改记录
<author> <time> <version > <desc>
***********************************************************/
//函数定义
//初始化链栈
void initStack(linkStack *lStack)
{
lStack->top = NULL;
lStack->count = 0;
}
/*************************************************
@ Function: pushStack
@ Description:
入栈操作,头插法
@ Input:
lStack //栈指针
elemt //入栈元素指针
size //入栈元素大小
@ Output:
@ Return:
入栈成功/失败
@ Others:
*************************************************/
returnCode pushStack(linkStack *lStack, pNode pushNode)
{
pNode Node = NULL;//栈节点
pNode temp = NULL;//临时节点
if (!pushNode||!lStack)return ERROR_POINT_NULL;//空指针
Node = (pNode)malloc(sizeof(lNode));
if (!Node) //申请空间失败
{
return ERROR_MALLOIC;
}
int size = sizeof(lNode);
memcpy(Node,pushNode,size);//数据拷贝到自己节点空间,如果不做数据类型大小修改,这里可以直接赋值
Node->next = NULL;
//头插法,插入节点
temp= lStack->top;
lStack->top = Node;
Node->next = temp;
lStack->count += 1;
return TRUE_CODE;
}
/*************************************************
@ Function: popStack
@ Description:
出栈操作
@ Input:
lStack //栈指针
@ Output:
popNode //将出栈的元素赋值到该指针上
@ Return:
@ Others:
popNode指针空间需要开辟,以保证达到空间需求
*************************************************/
void popStack(linkStack *lStack, pNode popNode)
{
pNode tempNode = NULL;//栈节点
if (lStack->count <= 0||!lStack||!popNode)return;//栈空
//赋值节点元素数据
int size = sizeof(lNode);
memcpy(popNode, lStack->top, size);
popNode->next = NULL;
//释放节点空间
tempNode = lStack->top;
lStack->top = lStack->top->next;
free(tempNode);//
tempNode = NULL;
lStack->count -= 1;//计数
return ;
}
/*************************************************
@ Function: readStack
@ Description:
查看栈顶元素(不出栈)
@ Input:
lStack //栈指针
@ Output:
popNode //将栈顶的元素赋值到该指针上
@ Return:
@ Others:
popNode指针空间需要开辟,以保证达到空间需求
*************************************************/
void readStack(linkStack *lStack,pNode popNode)
{
pNode tempNode = NULL;//栈节点
if (lStack->count <= 0 || !lStack || !popNode)return;//栈空
//赋值节点元素数据
int size = sizeof(lNode);
memcpy(popNode, lStack->top, size);
popNode->next = NULL;
return;
}
//销毁栈
void freeStack(linkStack *lStack)
{
pNode temp = NULL;
temp = lStack->top;
while (temp)//栈内还有元素
{
lStack->top = lStack->top->next;
free(temp);
lStack->count--; //测试用
temp = lStack->top;
}
lStack->count = 0;
}
3.1 tou文件 turnRPN.h 定义表达式链表操作
/************************************************************
Copyright (C), 1988-1999
FileName: turnRPN.h 2021.01.02
Author:hzp Version :1.0
Description: //获取用户输入,并且转换、计算后缀表达式的模块
Version: // v1.0
History: // 历史修改记录
<author> <time> <version > <desc>
***********************************************************/
#ifndef _TURN_RPN_H_
#define _TURN_RPN_H_
#include "LStack.h"
#include<stdio.h>
//定义扫描标记,用于扫描中缀表达式字符串并且转换成中缀表达式链
#define PRE_NULL (0)//前面没有字符
#define PRE_SYMBOL (1)//前面字符为运算符类型
#define PRE_INT (2)//前面字符为整形
#define PRE_DOUBLE (3) //前面字符为浮点类型
//单链表结构
typedef struct calLink {
lNode* front;//链头
lNode* rear;//链尾
int count;
}calLink;
//函数
//初始化并且获取中缀表达式链表
returnCode initCalLink(calLink* link);
//尾插法添加链表
void addCalLink(calLink* link, pNode addNode);
//释放中缀表达式链表
void freeCalLink(calLink* link);
//根据中缀表达式链转换并计算结果
returnCode calMiddle(calLink* link, numberData *sumData);
//右括号处理
returnCode popBracket(calLink* link, linkStack* lStack);
//栈内的四则运算(括号不参与)
returnCode calFun(linkStack* opStack, linkStack* numStack, operatorKind kind);
//单个操作符计算(括号不参与)
returnCode singleFun(linkStack* numStack, operatorKind kind);
#endif // !_TURN_RPN_H_
3.2 源文件 turnRPN.c
/************************************************************
Copyright (C), 1988-1999
FileName: turnRPN.c 2021.01.02
Author:hzp Version :1.0
Description: //获取用户输入,并且转换、计算后缀表达式的模块
Version: // v1.0
History: // 历史修改记录
<author> <time> <version > <desc>
***********************************************************/
#include "turnRPN.h"
#include<math.h>
//定义运算符优先级(数组表示)根据运算符标识宏设置,宏文件LStack.h
const int OPPRIORITY[] = { 1,1,2,2,2,3,4,4 };
//函数
//初始化并且获取中缀表达式链表
returnCode initCalLink(calLink* link)
{
char getc='\n'; //用于接收输入
int preKind = PRE_NULL; //用于标记当前字符前的数据类型,以便当前字符的转换
int doubleLevel = 0;//小数点位数
//初始化链表
lNode node;
node.next = NULL;
link->count = 0;
link->front = NULL;
link->rear = NULL;
//接收输入的表达式并转换链表
while ((getc = getchar()) != '\n')
{
//分析每个字符
switch (getc)
{
case '+':
{
node.elementKind = OPREATOR;//链表节点为运算符
node.data.kind = ADD;
addCalLink(link, &node);//添加链表
preKind = PRE_SYMBOL;//链表前节点为运算符类型
}break;
case '-':
{
if (preKind == PRE_SYMBOL|| preKind==PRE_NULL)//-1=0-1,为后续后缀计算实现负数计算,在纯负号前添加0
{
node.elementKind = NUMBER;//链表节点为运算符
node.data.num = 0;
addCalLink(link, &node);//添加链表
}
node.elementKind = OPREATOR;//链表节点为运算符
node.data.kind = SUB;
addCalLink(link, &node);//添加链表
preKind = PRE_SYMBOL;//链表前节点为运算符类型
}break;
case '*':
{
node.elementKind = OPREATOR;//链表节点为运算符
node.data.kind = MUL;
addCalLink(link, &node);//添加链表
preKind = PRE_SYMBOL;//链表前节点为运算符类型
}break;
case '/':
{
node.elementKind = OPREATOR;//链表节点为运算符
node.data.kind = DIV;
addCalLink(link, &node);//添加链表
preKind = PRE_SYMBOL;//链表前节点为运算符类型
}break;
case '(':
{
node.elementKind = OPREATOR;//链表节点为运算符
node.data.kind =L_BRACKET;
addCalLink(link, &node);//添加链表
preKind = PRE_SYMBOL;//链表前节点为运算符类型
}break;
case ')':
{
node.elementKind = OPREATOR;//链表节点为运算符
node.data.kind = R_BRACKET;
addCalLink(link, &node);//添加链表
preKind = PRE_SYMBOL;//链表前节点为运算符类型
}break;
case '%':
{
node.elementKind = OPREATOR;//链表节点为运算符
node.data.kind = MOD;
addCalLink(link, &node);//添加链表
preKind = PRE_SYMBOL;//链表前节点为运算符类型
}break;
case '^':
{
node.elementKind = OPREATOR;//链表节点为运算符
node.data.kind = POW;
addCalLink(link, &node);//添加链表
preKind = PRE_SYMBOL;//链表前节点为运算符类型
}break;
case '.': //小数点情况需要做标记,说明后面字符为小数
{
if (preKind != PRE_INT) //小数点前必须为整数
{
freeCalLink(link);//释放已经建立的链表
return ERROR_POINT_DOUBLE;
}
else
{
preKind = PRE_DOUBLE;//链表前节点为浮点类型
}
}break;
case ' ':break; //空格不管
default: //数字情况
{
if (getc >= '0' && getc <= '9') //数字
{
numberData numChar = (numberData)getc - 48;//取当前字符对应的数字
if (preKind == PRE_SYMBOL|| preKind == PRE_NULL)//如果当前表达式字符前方是空或运算符,那么当前需要新建一个数字节点
{
node.elementKind = NUMBER;//链表节点为运算符
node.data.num = 0;//数值初始化0
node.data.num += numChar;
addCalLink(link, &node);//添加链表
preKind = PRE_INT;//链表前节点为整形类型
doubleLevel = 0;//目前小数点保留0位
}
else if (preKind == PRE_DOUBLE) //如果当前表达式字符前方是浮点,就取链尾浮点运算
{
doubleLevel++;//小数点保留加1位
for (int i = 0; i < doubleLevel; i++)numChar /= 10;
link->rear->data.num += numChar;
}
else //如果当前表达式前方是整数,就取链尾进行整数运算
{
link->rear->data.num = link->rear->data.num * 10 + numChar;
}
}
else //非法的符号
{
freeCalLink(link);//释放已经建立的链表
return ERROR_CHAR;
}
}
}
}
if (link->count <= 0)return ERROR_OTHER;
return TRUE_CODE;
}
//释放中缀表达式链表
void freeCalLink(calLink* link)
{
lNode* temp=NULL;
if (!link)return;
while (link->count > 0)
{
temp = link->front;
link->front = temp->next;
free(temp);
link->count--;
}
link->rear = NULL;
}
//尾插法添加链表
void addCalLink(calLink* link, pNode addNode)
{
if (!link || !addNode)return;
lNode* node = NULL;//链表节点
node = (pNode)malloc(sizeof(lNode));
memcpy(node, addNode, sizeof(lNode)); //复制数据
if (link->count > 0)
{
link->rear->next = node;
link->rear = link->rear->next;
link->rear->next = NULL;
}
else
{
link->front = node;
link->rear = link->front;
link->rear->next = NULL;
}
link->count++;
}
//右括号处理
returnCode popBracket( linkStack* opStack, linkStack* numStack)
{
int isFind = 0;//是否弹出了当前对应的左括号
lNode tempNode;
returnCode re=TRUE_CODE;//返回值
while (!isFind&&opStack->count>0)
{
readStack(opStack, &tempNode); // 查看栈顶元素是否为左括号
if (tempNode.data.kind == L_BRACKET)
{
popStack(opStack, &tempNode);
isFind = 1; break; //结束循环
}
else
{
if((re=calFun(opStack, numStack, tempNode.data.kind))!= TRUE_CODE)return re; //表达式有误
}
}
if (!isFind)return ERROR_BRACKET; //没找到最近匹配的左括号
return re;
}
//根据当前操作符进行出栈的四则运算(括号不参与)操作符栈没有入栈操作
returnCode calFun(linkStack* opStack, linkStack* numStack, operatorKind kind)
{
lNode opNode; //运算符栈节点
returnCode re = TRUE_CODE;//返回值
if (opStack->count <= 0)return re;//操作数栈空就返回,上层外部函数入栈
readStack(opStack, &opNode);//查看栈顶元素,不弹出
//弹出优先级不低于当前的运算符,计算
while ((OPPRIORITY[opNode.data.kind] >= OPPRIORITY[kind])&& (OPPRIORITY[opNode.data.kind] < OPPRIORITY[L_BRACKET]))
{
popStack(opStack, &opNode);//弹出栈
if ((re=singleFun(numStack, opNode.data.kind)) != TRUE_CODE)
{
return re; //当前符号计算失败
}
if (opStack->count <= 0)return re;//操作数栈空就返回,上层外部函数入栈
readStack(opStack, &opNode);//查看栈顶元素,不弹出
}
return re;
}
//单个操作符计算(括号不参与),*a=0,/a=0(单乘或单除都为0)
returnCode singleFun(linkStack* numStack, operatorKind kind)
{
lNode numRNode; //运算数栈节点,右操作数,用于存放第一个弹出的数
lNode numLNode; //左操作数,用于存放第二个弹出的数
numLNode.data.num = 0;//初始化符号
if (numStack->count <2 )return ERROR_NUM_DOUBLE; //操作数不够
popStack(numStack, &numRNode); //右操作数
popStack(numStack, &numLNode); //左操作数
switch (kind)
{
case ADD://加
{
numRNode.data.num = numLNode.data.num + numRNode.data.num;
pushStack(numStack, &numRNode);
}break;
case SUB://减
{
numRNode.data.num = numLNode.data.num - numRNode.data.num;
pushStack(numStack, &numRNode);
}break;
case MUL://乘
{
numRNode.data.num = numLNode.data.num * numRNode.data.num;
pushStack(numStack, &numRNode);
}break;
case DIV://除
{
if (numRNode.data.num == 0)return ERROR_SUB_ZERO; //分母不为0
numRNode.data.num = numLNode.data.num / numRNode.data.num;
pushStack(numStack, &numRNode);
}break;
case MOD://取模
{
if (numRNode.data.num == 0)return ERROR_MOD_ZERO; //模数不为0
numRNode.data.num = (int)numLNode.data.num %(int)numRNode.data.num;
pushStack(numStack, &numRNode);
}break;
case POW://幂
{
numRNode.data.num = pow(numLNode.data.num, numRNode.data.num);
pushStack(numStack, &numRNode);
}break;
default:return ERROR_CHAR;//存在不能运算的操作符
}
return TRUE_CODE;
}
//根据中缀表达式链转换并计算结果
returnCode calMiddle(calLink* link ,numberData *sumData)
{
//准备两个栈,一个操作数栈,一个运算符栈,中缀表达式存放在链表里
linkStack opStack; //运算符栈
linkStack numStack; //运算数栈
lNode tempNode; //临时栈节点
pNode pLinkNode; //链表节点
int count = 0;//链表节点数
returnCode re = TRUE_CODE;//返回值
if (!link || link->count <= 0)return ERROR_POINT_NULL;
count = link->count;
pLinkNode = link->front; //获取中缀链表头节点
initStack(&opStack);//初始化栈
initStack(&numStack);
//根据中缀表达式链表转换和计算
while (count-- && pLinkNode)//根据count数量从左到右遍历链表
{
if (pLinkNode->elementKind == OPREATOR)//运算符
{
if (pLinkNode->data.kind == R_BRACKET) //处理右括号
{
if ((re=popBracket(&opStack, &numStack)) != TRUE_CODE)
{
freeStack(&opStack); freeStack(&numStack);
return re;//表达式有误
}
}
else if (pLinkNode->data.kind < L_BRACKET)// 处理栈内非括号运算符
{
if ((re=calFun(&opStack, &numStack, pLinkNode->data.kind)) != TRUE_CODE)
{
freeStack(&opStack); freeStack(&numStack);
return re;//表达式有误
}
pushStack(&opStack, pLinkNode); //当前运算符入栈
}
else pushStack(&opStack, pLinkNode);//左括号直接入栈
}
else if (pLinkNode->elementKind == NUMBER)//运算数
{
pushStack(&numStack, pLinkNode); //入操作数栈
}
pLinkNode = pLinkNode->next;
}
while (opStack.count > 0)//遍历链表结束后,处理剩下的栈内操作符
{
popStack(&opStack, &tempNode);//出栈
if ((re=singleFun(&numStack, tempNode.data.kind)) != TRUE_CODE)
{
freeStack(&opStack); freeStack(&numStack);
return re;//表达式有误
}
}
if (numStack.count != 1)//最终运算符栈内自身一个元素才是正确的
{
freeStack(&opStack); freeStack(&numStack);
return ERROR_OTHER;//栈内结果有误
}
else
{
popStack(&numStack, &tempNode);
*sumData = tempNode.data.num; // 返回结果
freeStack(&opStack); freeStack(&numStack);
}
return re;
}
main
#include <stdio.h>
#include <stdlib.h>
#include"test.h"
#include "LStack.h" //栈
#include"turnRPN.h" //链表
int main()
{
while (1)
{
numberData sumData = 0;
calLink link;
returnCode re = TRUE_CODE;//返回值
printf("输入计算的表达式:");
if ((re = initCalLink(&link)) == TRUE_CODE)
{
re = calMiddle(&link, &sumData);//计算
}
if (re != TRUE_CODE)
{
printErrorKind(re);
}
else
{
printf("\n结果:%0.3f\n", sumData);
}
freeCalLink(&link);
rewind(stdin);
}
return 0;
}