纯c实现计算器 支持 +-*/%^(源码)

最近有些无聊,整理了以前的代码,添加了取模以及幂运算,之前的代码链接
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;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值