8.用C/C++實現一個科學計算器———(超級詳細完整,包含C/C++版本和Qt版本)

目錄

1.需求分析

2.主要難點——逆波蘭算法

     2.1 中綴表達式轉換爲後綴表達式

     2.2 後綴表達式的計算

3.編程實現

     3.1  C/C++版本

     3.2  Qt版本

4.參考資料

 


1.需求分析

編程實現一個科學計算器(類似於Windows自帶的計算器),要求能夠實現加減乘除混合運算,並且能夠識別括號,優先級正確。

下面是本博客的Qt版本的計算器效果圖

2.主要難點——逆波蘭算法

     2.1 中綴表達式轉換爲後綴表達式

我們日常所用的數學表達式(如5+3)都是中綴表達式,中綴表達式是人容易理解的表達式。後綴表達式又叫做逆波蘭表達式,對計算機來說,計算中綴表達式是很困難的,但是計算後綴表達式卻非常容易,所以我們先把中綴表達式轉化成後綴表達式來計算。下面的動態圖和算法流程圖可以很好的演示整個轉換的過程:

                                                      圖1   中綴表達式轉後綴表達式的動態圖    

    

                                                            圖2   中綴表達式轉後綴表達式的流程圖

     2.2 後綴表達式的計算

後綴表達式的計算是比較簡單的,基本思路就是遇到操作符就將操作數出棧並根據操作符進行計算,並將結果進棧,如果沒有遇到操作符,就直接將操作數進棧。下圖是具體的流程圖,注意下面的代碼使用'\0'作爲表達式的終止符號(本人偷懶使用了別人的流程圖)

                                                          圖3   後綴表達式的計算---動態圖  

                 

                                                             圖4   後綴表達式的計算----流程圖 

3.編程實現

     3.1  C/C++版本

在編程的時候,剛開始準備使用C語言,編程的過程中發現,在中綴轉後綴表達式的時候需要一個存放字符型元素的棧,而後綴表達式的計算中又需要一個存放double型元素的棧,這樣一來,就需要分別編寫兩個棧,很麻煩。下面的代碼是直接用C++的類模板來實現的,程序比C語言簡單很多,當然純粹用C語言肯定也是沒有任何問題的。

實際編程中還需要考慮正負號,因爲‘+’和‘-’有時候不表示加減,爲了解決這個問題,可以將負數-a看成是0-a,把正數+a看成0+a,這樣一來,正負號的問題也解決了。

(1)類的聲明    calculator.h

#ifndef CALCULATOR_H
#define CALCULATOR_H

enum MAXSIZE
{
    STACK_INIT_SIZE=20,//定義初始最大容量
    STACKINCREMENT=10,//棧滿的時候,動態增加容量,每次增加10個元素空間
    MAXBUFFER=10,//最大緩衝區
    MAX_EXP_LEN=100//表達式最長爲100
};

template<typename ElemType>
class Calculator
{
public:
    struct sqStack
    {
        ElemType *base;//指向棧頂
        ElemType *top;
        int stackSize;//當前棧的最大容量
    };
    Calculator();
    ~Calculator();
    void Push(ElemType e);
    bool Pop(ElemType &e);
    void clearStack();
    int StackLen();

    int Calculation(char Postfix[]);//後綴表達式的計算
    bool Infix2Postfix(char Infix[],char Postfix[]);//中綴表達式變爲後綴表達式

private:
    sqStack s;

};

#endif // CALCULATOR_H

(2)類的實現  calculator.cpp

#include "calculator.h"
#include <stdio.h>

template<typename ElemType>
Calculator<ElemType>::Calculator()
{
    s.base=new ElemType[STACK_INIT_SIZE];//棧底指向申請空間的首地址
    if(s.base==NULL)//申請失敗
        exit(0);
    s.top=s.base;//top總是指向有效元素的下一個空間(棧頂),top中沒有數據
    s.stackSize=STACK_INIT_SIZE;
}

//銷燬棧,將內存空間釋放
template<typename ElemType>
Calculator<ElemType>::~Calculator()
{
    delete []s.base;
}

template<typename ElemType>
void Calculator<ElemType>::Push(ElemType e)
{
    if(s.top-s.base>=s.stackSize)
    {
        s.base=(ElemType *)realloc(s.base,(s.stackSize+STACKINCREMENT)*sizeof(ElemType));
        //        realloc是申請一個新的空間,並將舊的內容拷貝到新的空間,還會釋放以前的空間
        if(s.base==NULL)
            exit(0);
        s.top=s.base+s.stackSize;//因爲重新分配了空間,所以重新設置棧頂
        s.stackSize=s.stackSize+STACKINCREMENT;//當前棧的最大容量變大了
    }
    *(s.top)=e;
    s.top++;
}

template<typename ElemType>
bool Calculator<ElemType>::Pop(ElemType &e)
{
    if(s.top==s.base)
        return false;//空棧
    e=*(--(s.top));
    return true;
}

//清空棧,不改變物理空間
template<typename ElemType>
void Calculator<ElemType>::clearStack()
{
    s.top=s.base;
}

//計算棧的當前容量(存儲的數據量或者元素個數)
template<typename ElemType>
int Calculator<ElemType>::StackLen()
{
    return s.top-s.base;
}

template<typename ElemType>
int Calculator<ElemType>::Calculation(char Postfix[])
{
    int i=0,j;
    char c;
    char str[MAXBUFFER];
    double a=0,b=0;

    for(j=0;Postfix[j]!='\0';j++)
    {
        //        c=Postfix[j];
        while ((Postfix[j]>=48)&&(Postfix[j]<=57)||Postfix[j]=='.') //輸入的是數字
        {
            str[i]=Postfix[j];
//            printf("str[%d]=%c\n",i,c);
            i++;
            str[i]='\0';
            if(i>=10)
            {
                printf("出錯,輸入的數據長度過大!\n");
                return -1;
            }

            //            scanf("%c",&c);
            j++;
            if((Postfix[j]==' '))
            {
                //                str[i]='\0';
//                printf("str[%d]=%c\n",i,Postfix[j]);
                a=atof(str);
//                printf("%f \n",a);
                Push(a);
                i=0;
            }

        }

        switch (Postfix[j])
        {
        case '+':
            Pop(a);
            if(!Pop(b))//防止這是符號位(單目運算符)
            {
                 Push(a);
                 break;
            }
            Pop(b);
//            printf("%f+%f=%f\n",b,a,b+a);
            Push(b+a);
            break;
        case '-':
            Pop(a);
            if(!Pop(b))//
            {
                 Push(-a);
                 break;
            }
//            printf("%f-%f=%f\n",b,a,b-a);
            Push(b-a);
            break;
        case '*':
            Pop(a);
            Pop(b);
//            printf("%f*%f=%f\n",b,a,b*a);
            Push(b*a);
            break;
        case '/':
            Pop(a);
            if(a==0)
            {
                printf("除數不能爲零 !\n");
                return -1;
            }
            Pop(b);
            Push(b/a);

            break;
        default:
            break;
        }
    }
    Pop(a);
    return a;

}

template<typename ElemType>
bool Calculator<ElemType>::Infix2Postfix(char Infix[],char Postfix[])
{
    Calculator<char> s;
    int i=0,j=0;
    char e;

    printf("中綴表達式爲:");
    while (Infix[j]!='\0')
    {
        while(Infix[j]>='0' && Infix[j]<='9')
        {
            printf("%c",Infix[j]);
            Postfix[i++]=Infix[j];
            j++;
            if(Infix[j]<'0' || Infix[j]>'9')
            {
                Postfix[i++]=' ';
                printf(" ");
            }
        }

        switch (Infix[j])
        {

        case ')':
            s.Pop(e);
            while ('('!=e)
            {
                printf("%c ",e);
                Postfix[i++]=e;
                Postfix[i++]=' ';
                s.Pop(e);
            }
            break;

        case '+':
        case '-':
            if(0==s.StackLen())
                s.Push(Infix[j]);
            else
            {
                do
                {
                    s.Pop(e);
                    if('('==e)
                    {
                        s.Push(e);
                    }
                    else
                    {
                        printf("%c ",e);
                        Postfix[i++]=e;
                        Postfix[i++]=' ';
                    }
                }while (s.StackLen() && '('!=e);
                s.Push(Infix[j]);
            }
            break;

        case '*':
        case '/':
        case '(':
            s.Push(Infix[j]);
            break;

        case '\0':
            break;

        default:
            printf("\n輸入格式錯誤!\n");
            return -1;
        }

        if('\0'==Infix[j])
            break;
        j++;
    }

    while (s.StackLen())
    {
        s.Pop(e);
        printf("%c ",e);
        Postfix[i++]=e;
        Postfix[i++]=' ';
    }
    Postfix[i]='\0';
    printf("\n");
    return true;
}

(3)測試程序 main.cpp

#include <iostream>
#include "calculator.cpp"
#include <stdio.h>
using namespace std;

int main()
{
    Calculator<double> cal;
    char Infix[MAX_EXP_LEN],Postfix[MAX_EXP_LEN];
    gets(Infix);
    double sum;

    cal.Infix2Postfix(Infix,Postfix);
    sum=cal.Calculation(Postfix);
    printf("最終計算結果爲:%f\n\n",sum);
    return 0;
}

 (4)測試結果

下面計算8+(6-3)*(-5)+10/2:

     3.2  Qt版本

Qt實現的科學計算器有較友好的界面,效果圖在本文的開頭。

Qt的編程比較複雜一些,在這個程序裏面不僅添加了界面,而且還增加了清除數據,後退一個數字等功能,用了四個文件來編寫程序,基波思路跟上面的C++類似,由於程序太長,這裏就不放代碼了。我已經將完整的源代碼上傳,又需要的小夥伴可以前去下載(點擊進入下載界面),沒有積分的小夥伴可以私聊我。

4.參考資料

[1] 流程圖來自https://blog.csdn.net/hackerain/article/details/7682891?locationNum=15

[2]中綴表達式轉後綴表達式的動態圖來自https://www.cnblogs.com/lulipro/p/7450886.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章