当前位置: 首页 > news >正文

如何再工商局网站做设备抵押/谷歌浏览器引擎入口

如何再工商局网站做设备抵押,谷歌浏览器引擎入口,用手机做网站的软件,阿里云做网站选择服务器1、本篇要实现的内容 最近,大家讨论计算器的实现比较热,今天我也来用C和Visual Studio实现一个计算器的小程序。这里使用逆波兰算法,能够根据当前用户输入的算式表达式字符串,计算出所要的结果,算式字符串可以包括加、…

1、本篇要实现的内容

最近,大家讨论计算器的实现比较热,今天我也来用C++和Visual Studio实现一个计算器的小程序。这里使用逆波兰算法,能够根据当前用户输入的算式表达式字符串,计算出所要的结果,算式字符串可以包括加、减、乘、除和括号,支持整数、小数,鼠标和键盘均可操作,实现了一个较为经典的计算器功能。后期如果有时间我们再实现一些更多的计算器功能。本篇实现的效果如下:

在这里插入图片描述

2、设计目标

我们今天想制作一个计算器,需要基本上能达到日常使用的需求。首先它得有可操作的图形窗口界面,它要能够满足我们一些基本的计算需求,如整数和小数的加、减、乘、除,顺便再把括号功能附加上。同时我们在设计的时候,还允许用户输入算式表达式字符串,程序能根据用户输入算式表达式字符串,经过一些智能纠错后,对纠错后的算式表达式进行实时计算,并最终显示出结果。

2.1 、运行环境

操作系统:Windows10操作系统
编译环境:Microsoft Visual Studio 2010(VC6.0也可以直接编译运行)
其它事项:源代码仅仅包括一个cpp源文件,新建项目可直接编译运行,无需在资源编辑器中额外创建按键、显示框等控件资源。

2.2、实现图形化界面

首先计算器要方便使用,我们必须为它创建一个友好的图形界面。我们首先为他创建一个应用窗口,并为窗口添加相应的控件。控间最主要包括两大部分,一部分是用于用户输入的响应按键,另一部分是用于反馈用户输入和计算结果的显示控件。为了简化项目,我们这里采用系统CreateWindow函数创建的按键BUTTON控件和STATIC控件,分别来响应用户输入和输出。图形的区域分布如下:
在这里插入图片描述

2.3、实现字符串算式自动识别计算

通过字符串算式自动识别数学表达式有两个优点。第一个优点,可以方便用户随时校对自己输入算式表达式的正确性。在计算时我不仅仅需要看到的是计算后的得数,有时候我还需要看到我们已经输入的算术表达式,方便我校对输入的式子是否正确,如发现错误还可以及时修改。第二个优点,字符串算式表达式可以考虑到算式计算的优先级。在普通没有字符串表达式的计算器中,我们每输入一个算术符号和数字,就必须要计算出这一步的结果。如此循环操作,再往下继续输入运算符号和数字,屏幕只显示当前的结果。那么这样就势必无法考虑到加减乘除运算规则,只能根据用户输入算式的先后顺序计算,更没有办法考虑到括号的优先运算。那么字符串算数表达式就可以完美解决这个问题,这里还要用到逆波兰算法。
在这里插入图片描述
在这个算式中我们需要先计算3*13=39的乘法,在计算12+39=51的加法。

2.4、支持加、减、乘、除和括号

由于使用了逆波兰算法,这里运算我们支持加减乘除,还添加了对括号的支持。我们采用了字符串算式格式,我们可以方便的对加减乘除和括号的运算规则进行支持。因为在日常的运算中,如果拿着计算器还需要自己去考虑一个算式的运算顺序的话,会是一个很糟糕的体验。

在这里插入图片描述

2.5、实时更新运算结果

我们在我们在制作计算机前期构想的时候,借鉴了手机上自带计算器功能的一些创意,用户每输入一个字符都会更新并影响到最终结果。在使用计算器的时候,当用户每输入一个数字或者符号时,计算器都会根据当前已经输入的算式表达式,进行智能分析,预估出用户可能需要的结果,随即实时计算出结果并显示。

在这里插入图片描述

2.6、智能运算符号校验

我们是采用对字符串进行逆波兰法计算,并且是实时(每输入一个数字或字符都会影响到结果)计算,因此对算式字符串的规范性检测要求较高。但是我们日常在输入字符串表达式的时候,难免会存在一些手误,比如说连续输入两个乘号等等,那么这类的错误操作就需要我们用用户输入逻辑去加以规范或限制。同时还有用户在输入括号时,表达式中的左右括号数量不一致等问题,将会导致计算出现错误。我们这里通过输入逻辑检测解决了用户输入表达式的规范性。
在这里插入图片描述

2.7、错误判断提示

在遇到除数为零的特殊情况时,我们需要在结果中输出错误提示,否则计算会出现意外。如下图:

在这里插入图片描述

2.8、支持整数、小数运算

这里我们要双精度数据类型进行计算,确保计算的准确性。对小数的计算是我们日常生活中不可少的,部分计算器并没有增加对小数的支持。本次在程序设计的开始,就考虑到了这一点。这里包括对有限小数的计算,包括对循环小数的计算,以及无限循环小数结果的显示逻辑。
在这里插入图片描述

2.9、使用逆波兰算法计算数学表达式

一. 波兰式(前缀表达式)

波兰逻辑学家J.Lukasiewicz于1929年提出的表示表达式的一种方式,即二元运算符至于运算数之前的一种表达方式。

二.中缀表达式

普通的表示表达式的一种方法,将二元运算符置于运算数中间,也是大多数情况下使用的一种方法。

三.逆波兰式(后缀表达式)

与波兰式相反,是二元运算符置于运算数之后的一种表达方式。每一运算符都置于其运算对象之后,故称为后缀表示。

三种表达式的形象实例如下:

在这里插入图片描述

逆波兰式的应用——算术表达式求值

逆波兰式,也称逆波兰记法(Reverse Polish Notation)。在数据结构中,使用栈的概念完成表达式的求值操作,在计算机系统处理表达式的计算过程中,将中缀表达式转换为后缀表达式的形式进行解析转换并实施计算,这就是逆波兰算法的应用。

具体实现方法大致为:

  1. 设两个栈,操作数栈和运算符栈;
  2. 操作数依次入操作数栈;
  3. 运算符入栈前与运算符的栈顶运算符比较优先级;
  4. 优先级高于栈顶运算符,压入栈,读入下一个符号;
  5. 优先级低于栈顶运算符,栈顶运算符出栈,操作数栈退出两个操作数,进行运算,结果压入操作数栈;
  6. 优先级相等,左右括号相遇,栈顶运算符出栈即可;
  7. 后缀表达式读完,栈顶为运算结果。

在这里插入图片描述

2.10、支持背景图片

程序设计了一个简单的游戏背景设定,程序当前文件夹中放置名为bg.bmp的图片文件后,程序会自动加载并居中显示背景图片,大家可以放上自己喜欢的背景图片。

在这里插入图片描述

3、源码下载

该源码可以在VS2010和VC6.0中无差异运行,因此就上传了两个版本的源码,方便运行。

3.1、VS2010源码下载

CSDN下载地址:Calculator20241207-15-vs2010.rar

3.2、VC6.0源码下载

CSDN下载地址:Calculator20241207-15-vc6.0.rar

4、源代码实现过程

我们根据实现功能的不同,可以大致将整个项目分为以下各个模块。

4.1、链表栈的实现

由于逆波兰法会要用到栈操作,我们预先定义一个链栈,在字符串表达式计算过程中会频繁出栈和进栈,已经栈的初始化和销毁,要注意内存泄露。

//加载系统头文件#include "windows.h"#include "stdio.h"#include "math.h"//节点统计数字int	st_StackNodeNum=0;//链栈template<typename Type>struct Stack
{Type num;Stack<Type>* ptNext;};//初始化栈template<typename Type>void InitStack(Stack<Type>*& Node)
{Node = (Stack<Type>*)malloc(sizeof(Stack<Type>));Node->ptNext = NULL;st_StackNodeNum++;}//头插法入栈template<typename Type>void PushStack(Stack<Type>*& Node, Type value)
{Stack<Type>* pt = (Stack<Type>*)malloc(sizeof(Stack<Type>));pt->num = value;pt->ptNext = Node->ptNext;Node->ptNext = pt;st_StackNodeNum++;}//头插法出栈template<typename Type>void PopStack(Stack<Type>*& Node, Type& value)
{Stack<Type>* pt = Node->ptNext;value = pt->num;Node->ptNext = pt->ptNext;delete pt;st_StackNodeNum--;}//头插法出栈template<typename Type>void DestroyStack(Stack<Type>*& Node)
{if(Node->ptNext == NULL){delete Node;Node=NULL;st_StackNodeNum--;}}//判断栈是否为空,除去没有存数据的首个节点外template<typename Type>bool IsStackEmpty(Stack<Type>* Node)
{return Node->ptNext == NULL;}//获取栈顶部节点的数据template<typename Type>Type GetStackTopValue(Stack<Type>* Node)
{if(Node->ptNext !=NULL){return Node->ptNext->num;}else{return 0;}}

4.2、字符串操作函数

在字符表达式的输入和处理过程中,会遇到一些必须的字符处理函数,我们在这里定义。


//省略掉数字的小数点后末尾多余的零void	TrimBackZero(char *szString)
{//标记小数点的位置int iDotPos=-1;//先找到小数点的位置for(int i=0;i<lstrlen(szString);i++){if(szString[i]=='.'){iDotPos=i;break;}}//寻找末尾多余的零for(int j=lstrlen(szString)-1;j>=iDotPos;j--){if(szString[j]=='.' || szString[j]=='0'){szString[j]='\0';}else{break;}}}//获取字符串中某个字符的个数int		GetCharAmount(char *szString,char sign)
{int iAmount=0;for(int i=0;i<lstrlen(szString);i++){if(szString[i]==sign)iAmount++;}return iAmount;}//判断是否为数字bool	IsNumber(char *szString)
{if(strcmp(szString,"0")==0)return true;if(strcmp(szString,"1")==0)return true;if(strcmp(szString,"2")==0)return true;if(strcmp(szString,"3")==0)return true;if(strcmp(szString,"4")==0)return true;if(strcmp(szString,"5")==0)return true;if(strcmp(szString,"6")==0)return true;if(strcmp(szString,"7")==0)return true;if(strcmp(szString,"8")==0)return true;if(strcmp(szString,"9")==0)return true;return false;}//判断是否为运算符号bool	IsOperator(char *szString)
{if(strcmp(szString,"+")==0)return true;if(strcmp(szString,"-")==0)return true;if(strcmp(szString,"*")==0)return true;if(strcmp(szString,"/")==0)return true;return false;}

4.3、计算器类

为了实现计算器的各个功能,我们集成到一个计算器类中进行操作。


//按键最大数量#define	BUTTONMAXNUM	20//计算器类class Calculator
{public://用于保存算式表达式字符串char	szExpression[1024];//用于保存经过校验的算式表达式字符串char	szCheckedExpression[1024];//用于保存计算结果的字符串char	szResult[1024];//控件字体设置HFONT	hCtlFont;//用于存储双精度格式的结果double	ResultDate;//标记是否出现错误bool	tagError;//记录错误信息char	szErrorMessage[1024];//背景图片HBITMAP	hBackGroundBitmap;public:Calculator();~Calculator();//初始化,用于创建按键控件和显示控件void	Initialize(HWND hWnd);//相应键盘输入转换成统一的指令(鼠标点击按键)void	OnCommand(HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam);//相应键盘输入转换成统一的指令(数字按键和运算符号按键)void	OnChar(HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam);//相应键盘输入转换成统一的指令(其他特殊按键)void	OnKeyDown(HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam);//根据用户的输入指令进行相应的处理void	OnExcuteString(HWND hWnd,char *szCommand);//屏幕显示内容void	OnPaint(HWND hWnd,HDC hDC);//根据字符串计算结果double	GetResultValueByString();//计算分步结果void	CalValue(Stack<double> *&ptNumStack,Stack<char> *&ptOperatorStack);//逆波兰算法实现double	Polish(char *String, int len);};//自定义计算器类实例Calculator Calculators;Calculator::Calculator()
{hCtlFont=NULL;strcpy(szExpression,"");strcpy(szCheckedExpression,"");strcpy(szResult,"");ResultDate=0;tagError=false;strcpy(szErrorMessage,"");hBackGroundBitmap=NULL;hBackGroundBitmap=(HBITMAP)LoadImage(NULL,"bg.bmp",IMAGE_BITMAP,0,0,LR_LOADFROMFILE);}Calculator::~Calculator()
{//删除字体资源DeleteObject(hCtlFont);}

4.3.1、初始化及界面初始化

我们这里分别采用系统CreateWindow函数创建的按键BUTTON控件和STATIC控件,分别来响应用户输入和输出。


void	Calculator::Initialize(HWND hWnd)
{//控件字体设置HFONT hCtlFont=CreateFont(22,0,0,0,1000,0,0,0,0,0,0,PROOF_QUALITY,0,"宋体");//获取窗口的大小RECT tempClientRect;GetClientRect(hWnd,&tempClientRect);//自定义按键的文字标题char szButtonTitle[BUTTONMAXNUM][1024]={".","0","C","+","1","2","3","-","4","5","6","*","7","8","9","/","(",")","DEL","="};//创建按键控件,并设置按键的位置和标题for(int i=0;i<BUTTONMAXNUM;i++){//设置按键的宽和高int w=60,h=35,gap=10;//设置按键的坐标位置int x=10+(i%4)*(w+gap),y=tempClientRect.bottom-h-gap-(i/4)*(h+gap);//创建按键子控件CreateWindow("BUTTON",szButtonTitle[i],WS_CHILD|WS_VISIBLE|WS_CLIPCHILDREN|WS_CLIPCHILDREN|WS_CLIPSIBLINGS,x,y,60,35,hWnd,(HMENU)i,NULL,NULL);//设置字体记大小SendMessage(GetDlgItem(hWnd,i),WM_SETFONT,(WPARAM)hCtlFont,1);}//创建显示子控件,算式显示屏幕CreateWindowEx(WS_EX_CLIENTEDGE,"STATIC","",WS_CHILD|WS_VISIBLE|SS_RIGHT|SS_CENTERIMAGE,10,10,tempClientRect.right-20,50,hWnd,(HMENU)51,NULL,NULL);//创建显示子控件,结果显示屏幕CreateWindowEx(WS_EX_CLIENTEDGE,"STATIC","",WS_CHILD|WS_VISIBLE|SS_RIGHT|SS_CENTERIMAGE,10,70,tempClientRect.right-20,50,hWnd,(HMENU)53,NULL,NULL);//设置字体记大小SendMessage(GetDlgItem(hWnd,51),WM_SETFONT,(WPARAM)hCtlFont,1);SendMessage(GetDlgItem(hWnd,52),WM_SETFONT,(WPARAM)hCtlFont,1);SendMessage(GetDlgItem(hWnd,53),WM_SETFONT,(WPARAM)hCtlFont,1);}

4.3.1、计算器消息处理逻辑

在这里,我们设计鼠标操作和键盘同时可以操作计算器,因此我们需要统一两种操作的模式。我们将WM_CHAR、WM_KEYDOWN和WM_COMMAND的消息统一转换成Calculator类的指令。

void	Calculator::OnCommand(HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam)
{char szButtonTitle[1024]="";GetWindowText(GetDlgItem(hWnd,LOWORD(wParam)),szButtonTitle,1024);OnExcuteString(hWnd,szButtonTitle);}void	Calculator::OnChar(HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam)
{//判断是否响应相应的按键bool tagResponseStatus=false;//当用户按下数字键,包括小键盘的数字键if('0'<=LOWORD(wParam) && LOWORD(wParam)<='9'){tagResponseStatus=true;}//当按下加、减、乘、除按键时响应if(LOWORD(wParam)==43 || LOWORD(wParam)==45 || LOWORD(wParam)==42 || LOWORD(wParam)==47){tagResponseStatus=true;}//当按下左括号、右括号、小数点键if(LOWORD(wParam)==40 || LOWORD(wParam)==41 || LOWORD(wParam)==46){tagResponseStatus=true;}//对设置的按键命令进行响应if(tagResponseStatus==true){char szCommand[1024]="";sprintf(szCommand,"%c",LOWORD(wParam));OnExcuteString(hWnd,szCommand);}//当按下回车、ESC、等号、BACKSPACE键执行相应的指令if(LOWORD(wParam)==13){OnExcuteString(hWnd,"RETURN");}if(LOWORD(wParam)==27){OnExcuteString(hWnd,"ESC");}if(LOWORD(wParam)==61){OnExcuteString(hWnd,"=");}if(LOWORD(wParam)==8){OnExcuteString(hWnd,"DEL");}}void	Calculator::OnKeyDown(HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam)
{//键盘按下DEL键执行清空显示控件的指令if(LOWORD(wParam)==46){OnExcuteString(hWnd,"C");}}//屏幕显示内容void	Calculator::OnPaint(HWND hWnd,HDC hDC)
{//显示背景颜色if(hBackGroundBitmap!=NULL){BITMAP BM;RECT tempClientRect;GetClientRect(hWnd,&tempClientRect);HDC hTemDC=::CreateCompatibleDC(hDC);SelectObject(hTemDC,hBackGroundBitmap);GetObject(hBackGroundBitmap,sizeof(BITMAP),&BM);BitBlt(hDC,0,0,tempClientRect.right,tempClientRect.bottom,hTemDC,(BM.bmWidth-tempClientRect.right)/2,(BM.bmHeight-tempClientRect.bottom)/2,SRCCOPY);DeleteDC(hTemDC);}//调试信息,防止内存泄露if(!true){char szTemp[1024]="";sprintf(szTemp,"st_StackNodeNum:%d",st_StackNodeNum);TextOut(hDC,10,120,szTemp,strlen(szTemp));}}

4.3.2、用户自定义输入表达式逻辑

在计算器字符表达式的输入过程中,我们需要用户根据一定的规则去输入中缀表达式,而不能任意输入错误的表达式,我们会在用户输入时加上一些必要的校验,比如不能连续出现两个运算符号,括号需要成对出现等等。


void	Calculator::OnExcuteString(HWND hWnd,char *szCommand)
{//每次输入时重置错误信息tagError=false;strcpy(szErrorMessage,"");//如果当前需要添加的是数字if(IsNumber(szCommand)==true){if(strlen(szExpression)>0 && strlen(szExpression)<500){//获取表达式的最后一个字符并转换为字符串char szEndChar[10]="";sprintf(szEndChar,"%c",szExpression[strlen(szExpression)-1]);//如果末尾不为右括号则直接添加if(strcmp(szEndChar,")")!=0){strcat(szExpression,szCommand);}}else{strcat(szExpression,szCommand);}}//如果当前需要添加的是运算符if(IsOperator(szCommand)==true){if(strlen(szExpression)>0 && strlen(szExpression)<500){//获取表达式的最后一个字符并转换为字符串char szEndChar[10]="";sprintf(szEndChar,"%c",szExpression[strlen(szExpression)-1]);//判断字符串结尾字符是否为数字if(IsNumber(szEndChar)==true || strcmp(szEndChar,")")==0 || strcmp(szEndChar,".")==0){strcat(szExpression,szCommand);}//如果末尾字符为运算符,删除用新的运算符替换旧的运算符else if(IsOperator(szEndChar)==true){//在新的字符串中进行操作char szNewExpression[1024]="";//拷贝到新的字符串进行操作strcpy(szNewExpression,szExpression);//删除一个字符szNewExpression[strlen(szNewExpression)-1]='\0';//添加新的运算符号strcat(szNewExpression,szCommand);//拷贝新的字符串到原算式表达式字符串strcpy(szExpression,szNewExpression);}}}//左括号的输入if(strcmp(szCommand,"(")==0){if(strlen(szExpression)<500){//获取表达式的最后一个字符并转换为字符串char szEndChar[10]="";sprintf(szEndChar,"%c",szExpression[strlen(szExpression)-1]);//末尾字符为运算数字运算符的替换,为数字(或其他)的则直接添加if(strlen(szExpression)==0 || IsOperator(szEndChar)==true || strcmp(szEndChar,"(")==0){strcat(szExpression,szCommand);}else{MessageBeep(MB_OK);}}}//右括号的输入if(strcmp(szCommand,")")==0){if(strlen(szExpression)<500){//获取表达式的最后一个字符并转换为字符串char szEndChar[10]="";sprintf(szEndChar,"%c",szExpression[strlen(szExpression)-1]);//判断字符串结尾字符是否为数字if((IsNumber(szEndChar)==true || strcmp(szEndChar,")")==0) && GetCharAmount(szExpression,'(')>GetCharAmount(szExpression,')')){strcat(szExpression,szCommand);}	else{MessageBeep(MB_OK);}	}}//如果前一个字符是数字,则直接添加到算式中if(strcmp(szCommand,".")==0){if(strlen(szExpression)<500){//获取表达式的最后一个字符并转换为字符串char szEndChar[10]="";sprintf(szEndChar,"%c",szExpression[strlen(szExpression)-1]);//判断字符串结尾字符是否为数字if(IsNumber(szEndChar)==true){strcat(szExpression,szCommand);}}}//如果是数字,则直接添加到算式显示控件if(strcmp(szCommand,"C")==0){strcpy(szExpression,"");strcpy(szCheckedExpression,"");strcpy(szResult,"");}//如果是数字,则直接添加到算式显示控件if(strcmp(szCommand,"DEL")==0){if(strlen(szExpression)>0){//获取表达式的最后一个字符并转换为字符串char szEndChar[10]="";sprintf(szEndChar,"%c",szExpression[strlen(szExpression)-1]);//在新的字符串中进行操作char szNewExpression[1024]="";//拷贝到新的字符串进行操作strcpy(szNewExpression,szExpression);//删除一个字符szNewExpression[strlen(szNewExpression)-1]='\0';//拷贝新的字符串到原算式表达式字符串strcpy(szExpression,szNewExpression);}else{MessageBeep(MB_OK);}}//根据用户输入的字符串表达式智能纠错后计算结果GetResultValueByString();//当按下等于号对结果进行交换保存if(strcmp(szCommand,"=")==0){if(tagError!=true){//将结果保存到算式表达式字符串,并重置其他字符串strcpy(szExpression,szResult);strcpy(szCheckedExpression,"");strcpy(szResult,"");}else{MessageBeep(MB_OK);}}//更新显示“ 字符串显示框”SetWindowText(GetDlgItem(hWnd,51),szExpression);//更新显示校验后的“ 字符串显示框”SetWindowText(GetDlgItem(hWnd,52),szCheckedExpression);//更新显示“ 字符串显示框”SetWindowText(GetDlgItem(hWnd,53),szResult);//更新界面//InvalidateRect(hWnd,NULL,false);}

4.3.3、逆波兰计算数学表达式

采用将用户自定义输入的中缀表达式字符串,通过栈的方式转换为后缀表达式的算法,即逆波兰方法及时并返回结果。


void	Calculator::CalValue(Stack<double> *&ptNumStack,Stack<char> *&ptOperatorStack)
{double NumberLeft, NumberRight, NumberResult;char Operator;//将栈顶的两个数字和一个操作符进行出栈操作PopStack(ptNumStack, NumberRight);PopStack(ptNumStack, NumberLeft);PopStack(ptOperatorStack, Operator);//记录除数为零的情况if(NumberRight==0 || NumberLeft==0){tagError=true;strcpy(szErrorMessage,"错误:除数不能为零");}//对出栈的两个数字和一个操作符进行计算if (Operator == '+')NumberResult = NumberLeft + NumberRight;if (Operator == '-')NumberResult = NumberLeft - NumberRight;if (Operator == '*')NumberResult = NumberLeft * NumberRight;if (Operator == '/')NumberResult = NumberLeft / NumberRight;//将计算后的结果继续押入栈中PushStack(ptNumStack, NumberResult);}//逆波兰算法实现double	Calculator::Polish(char *String, int len)
{Stack<double> *ptNumStack;Stack<char> *ptOperatorStack;//初始栈,最主要产生一个默认的节点InitStack(ptNumStack);InitStack(ptOperatorStack);//逐字符读取字符串的游标位置int index = 0;//逐字符读取字符串while(!IsStackEmpty(ptOperatorStack) || index<len){//当前游标位置小于字符串长度时,逐个获取数字和运算符号并进行运算;否则做收尾工作if(index<len){//如果当前游标位置为数字,则说明这里是我们需要读取数字的开始位置if((String[index] >= '0' && String[index] <= '9') || String[index]=='.'){//将此后的数字区域读取到临时字符串char szTempNum[100]="";int iPos=0;//循环取得数字,当遇到第一个不是数字或小数点的字符for(int i=index;i<len;i++){if((String[i] >= '0' && String[i] <= '9') || String[i]=='.'){szTempNum[iPos++]=String[i];index++;}else{break;}}szTempNum[iPos]='\0';//获取到我们需要的数字,并将数字保存到栈中double tempNumber=atof(szTempNum);PushStack(ptNumStack, tempNumber);}else {//如果当前字符串为运算符,则根据运行符的种类进行判断if (String[index] == '(' || (GetStackTopValue(ptOperatorStack) == '(' && String[index] != ')') || IsStackEmpty(ptOperatorStack)){//遇到以上情况,则直接将运行符保存的符号栈中PushStack(ptOperatorStack, String[index++]);}else if (String[index] == '+' || String[index] == '-'){//如果遇到加、减号,就把此前已入栈的算式进行计算,并将结果结果和符号重新入栈while (GetStackTopValue(ptOperatorStack) != '(' && !IsStackEmpty(ptOperatorStack)){CalValue(ptNumStack, ptOperatorStack);}PushStack(ptOperatorStack, String[index++]);}else if (String[index] == '*' || String[index] == '/')				{//如果遇到乘、除号,则把之前所有乘、除相关的算式进行计算,并将结果结果和符号重新入栈while (GetStackTopValue(ptOperatorStack) == '*' || GetStackTopValue(ptOperatorStack) == '/'){CalValue(ptNumStack, ptOperatorStack);}PushStack(ptOperatorStack, String[index++]);}else if (String[index] == ')'){//当遇到有括号,则把所有括号对内的算式计算完毕,右括号无需入栈while(GetStackTopValue(ptOperatorStack) != '(') {//当栈为空时无需进行计算跳出循环if(IsStackEmpty(ptOperatorStack))break;CalValue(ptNumStack, ptOperatorStack);}//当计算完所有括号内容的算式,弹出对应的左括号char tempBracket;PopStack(ptOperatorStack, tempBracket);index++;}}}else{//遍历完字符串所有字符后,只需对还未空的运算符栈进行逐步计算CalValue(ptNumStack, ptOperatorStack);}}//最后栈顶(根节点的下一个节点)的数据就是表达式的结果double NumberValue=0;PopStack(ptNumStack,NumberValue);//在删除所有子节点后销毁栈的根节点DestroyStack(ptNumStack);DestroyStack(ptOperatorStack);//返回计算的结果return NumberValue;}

4.3.3、更新及显示结果

前期已经通过键盘或鼠标消息处理,在szExpression中输入了用户自定义算式表达式,因此,我们直接在这里进行计算,并将最终的结果反馈到szResult字符串。并通过类的流程控制在显示控件中显示出来。


//根据字符串计算结果double	Calculator::GetResultValueByString()
{//校验用户的输入,进行自动纠错处理strcpy(szCheckedExpression,szExpression);//对客户输入的算式进行自动纠错处理if(strlen(szCheckedExpression)!=0){//自动去除末尾的非数字符号for(int i=strlen(szCheckedExpression)-1;i>=0;i--){//获取表达式的最后一个字符并转换为字符串char szEndChar[10]="";sprintf(szEndChar,"%c",szCheckedExpression[strlen(szCheckedExpression)-1]);//智能删除用户算式表达式的末尾运算符号和左括号if(IsNumber(szEndChar)==false && strcmp(szEndChar,")")!=0 && strcmp(szEndChar,".")!=0){szCheckedExpression[i]='\0';}else{break;}}//自动添加用户应加未加的右括号int iTempBracketNum=GetCharAmount(szCheckedExpression,'(')-GetCharAmount(szCheckedExpression,')');//自动添加用户应加未加的右括号for(int j=0;j<iTempBracketNum;j++){strcat(szCheckedExpression,")");}//将纠错后的字符串进行计算处理if(strlen(szCheckedExpression)!=0){//根据纠错后的算式字符串计算结果ResultDate=Polish(szCheckedExpression,strlen(szCheckedExpression));//显示出结果sprintf(szResult,"%0.10f",ResultDate);//删除小数部分末尾的零TrimBackZero(szResult);//如果出现错误,则只显示错误信息if(tagError==true){strcpy(szResult,szErrorMessage);}}}else{//用户自定义字符串为空时,重置结果字符串strcpy(szCheckedExpression,"");strcpy(szResult,"");}return 0;}

4.4、主窗口函数及消息循环

负责程序主窗口的创建,以及消息函数的集中分发处理。这里由于存在子按键控件,因此鼠标点击按键后,主窗口将无法收到键盘消息WM_CHAR和WM_KEYDIWN消息,导致键盘输入失效,我们这里采用在主消息循环中,复制子窗口WM_CHAR和WM_KEYDIWN消息并手动转发给游戏主窗口的方法予以解决。同时在使用键盘过程中要注意,在中文输入法时键盘输入受到一定影响,需要手动切换输入法。另外,小键盘锁也会影响到小键盘的输入。


//消息处理模块LRESULT CALLBACK WndProc(HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam)
{HDC hDC=NULL;switch(message){case WM_CREATE://初始化并创建按键、算式显示框和结果显示框Calculators.Initialize(hWnd);return 0;case WM_PAINT:PAINTSTRUCT PS;	hDC=BeginPaint(hWnd,&PS);//显示屏幕内容Calculators.OnPaint(hWnd,hDC);ReleaseDC(hWnd,hDC);return 0;case WM_COMMAND://根据消息执行计算器的操作Calculators.OnCommand(hWnd,message,wParam,lParam);return 0;case WM_CHAR://根据消息执行计算器的操作Calculators.OnChar(hWnd,message,wParam,lParam);return 0;case WM_KEYDOWN://根据消息执行计算器的操作Calculators.OnKeyDown(hWnd,message,wParam,lParam);return 0;case WM_DESTROY:PostQuitMessage(0);return 0;}return DefWindowProc(hWnd,message,wParam,lParam);}//
//Calculator计算器经典版
//作者:zhooyu
//2024.12.7
//CSDN主页地址:https://blog.csdn.net/zhooyu
//CSDN文章地址:https://blog.csdn.net/zhooyu/article/details/144202897
////主函数int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,PSTR szCmdLine,int iCmdShow)
{MSG		msg;HWND	hWnd;CHAR	szAppName[]="Calculator";//设置程序的样式WNDCLASS		WC;WC.style		= CS_HREDRAW|CS_VREDRAW;WC.lpfnWndProc		= WndProc;WC.cbClsExtra		= 0;WC.cbWndExtra		= 0;WC.hInstance		= hInstance;WC.hIcon		= LoadIcon(hInstance,IDI_APPLICATION);WC.hCursor		= LoadCursor(hInstance,IDC_ARROW);WC.hbrBackground	= (HBRUSH)GetStockObject(GRAY_BRUSH);WC.lpszMenuName		= NULL;WC.lpszClassName	= szAppName;if(!RegisterClass(&WC)){return 0;}//创建窗口hWnd=CreateWindow(szAppName,szAppName,WS_OVERLAPPEDWINDOW&~WS_THICKFRAME&~WS_MAXIMIZEBOX,CW_USEDEFAULT,CW_USEDEFAULT,295,390,NULL,NULL,hInstance,NULL);//显示更新窗口ShowWindow(hWnd,iCmdShow);UpdateWindow(hWnd);//消息循环while(GetMessage(&msg,NULL,0,0)){TranslateMessage(&msg);DispatchMessage(&msg);//调试信息if(msg.message==WM_CHAR || msg.message==WM_KEYDOWN){//调试信息if(!true){char szTemp[1024]="";sprintf(szTemp,"%d,%d",msg.hwnd,hWnd);MessageBox(NULL,szTemp,"",MB_OK);}//确保父窗口收到按键消息if(msg.hwnd!=hWnd){SendMessage(hWnd,msg.message,msg.wParam,msg.lParam);}}}return msg.wParam;}

5、完整源码

该项目代码仅仅包括一个cpp源文件,新建项目直接运行,无需在资源编辑器中创建按键、显示框等控件资源。可以在VS2010和VC6.0中新建项目后直接运行。这里将全部源代码整理如下,供大家参考。整理代码不易,请大家不吝点赞关注,如果能留言就再好不过了,您的支持是我继续前进的动力!!谢谢了先。

//加载系统头文件#include "windows.h"#include "stdio.h"#include "math.h"//节点统计数字int	st_StackNodeNum=0;//链栈template<typename Type>struct Stack
{Type num;Stack<Type>* ptNext;};//初始化栈template<typename Type>void InitStack(Stack<Type>*& Node)
{Node = (Stack<Type>*)malloc(sizeof(Stack<Type>));Node->ptNext = NULL;st_StackNodeNum++;}//头插法入栈template<typename Type>void PushStack(Stack<Type>*& Node, Type value)
{Stack<Type>* pt = (Stack<Type>*)malloc(sizeof(Stack<Type>));pt->num = value;pt->ptNext = Node->ptNext;Node->ptNext = pt;st_StackNodeNum++;}//头插法出栈template<typename Type>void PopStack(Stack<Type>*& Node, Type& value)
{Stack<Type>* pt = Node->ptNext;value = pt->num;Node->ptNext = pt->ptNext;delete pt;st_StackNodeNum--;}//头插法出栈template<typename Type>void DestroyStack(Stack<Type>*& Node)
{if(Node->ptNext == NULL){delete Node;Node=NULL;st_StackNodeNum--;}}//判断栈是否为空,除去没有存数据的首个节点外template<typename Type>bool IsStackEmpty(Stack<Type>* Node)
{return Node->ptNext == NULL;}//获取栈顶部节点的数据template<typename Type>Type GetStackTopValue(Stack<Type>* Node)
{if(Node->ptNext !=NULL){return Node->ptNext->num;}else{return 0;}}//省略掉数字的小数点后末尾多余的零void	TrimBackZero(char *szString)
{//标记小数点的位置int iDotPos=-1;//先找到小数点的位置for(int i=0;i<lstrlen(szString);i++){if(szString[i]=='.'){iDotPos=i;break;}}//寻找末尾多余的零for(int j=lstrlen(szString)-1;j>=iDotPos;j--){if(szString[j]=='.' || szString[j]=='0'){szString[j]='\0';}else{break;}}}//获取字符串中某个字符的个数int		GetCharAmount(char *szString,char sign)
{int iAmount=0;for(int i=0;i<lstrlen(szString);i++){if(szString[i]==sign)iAmount++;}return iAmount;}//判断是否为数字bool	IsNumber(char *szString)
{if(strcmp(szString,"0")==0)return true;if(strcmp(szString,"1")==0)return true;if(strcmp(szString,"2")==0)return true;if(strcmp(szString,"3")==0)return true;if(strcmp(szString,"4")==0)return true;if(strcmp(szString,"5")==0)return true;if(strcmp(szString,"6")==0)return true;if(strcmp(szString,"7")==0)return true;if(strcmp(szString,"8")==0)return true;if(strcmp(szString,"9")==0)return true;return false;}//判断是否为运算符号bool	IsOperator(char *szString)
{if(strcmp(szString,"+")==0)return true;if(strcmp(szString,"-")==0)return true;if(strcmp(szString,"*")==0)return true;if(strcmp(szString,"/")==0)return true;return false;}//按键最大数量#define	BUTTONMAXNUM	20//计算器类class Calculator
{public://用于保存算式表达式字符串char	szExpression[1024];//用于保存经过校验的算式表达式字符串char	szCheckedExpression[1024];//用于保存计算结果的字符串char	szResult[1024];//控件字体设置HFONT	hCtlFont;//用于存储双精度格式的结果double	ResultDate;//标记是否出现错误bool	tagError;//记录错误信息char	szErrorMessage[1024];//背景图片HBITMAP	hBackGroundBitmap;public:Calculator();~Calculator();//初始化,用于创建按键控件和显示控件void	Initialize(HWND hWnd);//相应键盘输入转换成统一的指令(鼠标点击按键)void	OnCommand(HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam);//相应键盘输入转换成统一的指令(数字按键和运算符号按键)void	OnChar(HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam);//相应键盘输入转换成统一的指令(其他特殊按键)void	OnKeyDown(HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam);//根据用户的输入指令进行相应的处理void	OnExcuteString(HWND hWnd,char *szCommand);//屏幕显示内容void	OnPaint(HWND hWnd,HDC hDC);//根据字符串计算结果double	GetResultValueByString();//计算分步结果void	CalValue(Stack<double> *&ptNumStack,Stack<char> *&ptOperatorStack);//逆波兰算法实现double	Polish(char *String, int len);};//自定义计算器类实例Calculator Calculators;Calculator::Calculator()
{hCtlFont=NULL;strcpy(szExpression,"");strcpy(szCheckedExpression,"");strcpy(szResult,"");ResultDate=0;tagError=false;strcpy(szErrorMessage,"");hBackGroundBitmap=NULL;hBackGroundBitmap=(HBITMAP)LoadImage(NULL,"bg.bmp",IMAGE_BITMAP,0,0,LR_LOADFROMFILE);}Calculator::~Calculator()
{//删除字体资源DeleteObject(hCtlFont);}void	Calculator::Initialize(HWND hWnd)
{//控件字体设置HFONT hCtlFont=CreateFont(22,0,0,0,1000,0,0,0,0,0,0,PROOF_QUALITY,0,"宋体");//获取窗口的大小RECT tempClientRect;GetClientRect(hWnd,&tempClientRect);//自定义按键的文字标题char szButtonTitle[BUTTONMAXNUM][1024]={".","0","C","+","1","2","3","-","4","5","6","*","7","8","9","/","(",")","DEL","="};//创建按键控件,并设置按键的位置和标题for(int i=0;i<BUTTONMAXNUM;i++){//设置按键的宽和高int w=60,h=35,gap=10;//设置按键的坐标位置int x=10+(i%4)*(w+gap),y=tempClientRect.bottom-h-gap-(i/4)*(h+gap);//创建按键子控件CreateWindow("BUTTON",szButtonTitle[i],WS_CHILD|WS_VISIBLE|WS_CLIPCHILDREN|WS_CLIPCHILDREN|WS_CLIPSIBLINGS,x,y,60,35,hWnd,(HMENU)i,NULL,NULL);//设置字体记大小SendMessage(GetDlgItem(hWnd,i),WM_SETFONT,(WPARAM)hCtlFont,1);}//创建显示子控件,算式显示屏幕CreateWindowEx(WS_EX_CLIENTEDGE,"STATIC","",WS_CHILD|WS_VISIBLE|SS_RIGHT|SS_CENTERIMAGE,10,10,tempClientRect.right-20,50,hWnd,(HMENU)51,NULL,NULL);//创建显示子控件,结果显示屏幕CreateWindowEx(WS_EX_CLIENTEDGE,"STATIC","",WS_CHILD|WS_VISIBLE|SS_RIGHT|SS_CENTERIMAGE,10,70,tempClientRect.right-20,50,hWnd,(HMENU)53,NULL,NULL);//设置字体记大小SendMessage(GetDlgItem(hWnd,51),WM_SETFONT,(WPARAM)hCtlFont,1);SendMessage(GetDlgItem(hWnd,52),WM_SETFONT,(WPARAM)hCtlFont,1);SendMessage(GetDlgItem(hWnd,53),WM_SETFONT,(WPARAM)hCtlFont,1);}void	Calculator::OnCommand(HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam)
{char szButtonTitle[1024]="";GetWindowText(GetDlgItem(hWnd,LOWORD(wParam)),szButtonTitle,1024);OnExcuteString(hWnd,szButtonTitle);}void	Calculator::OnChar(HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam)
{//判断是否响应相应的按键bool tagResponseStatus=false;//当用户按下数字键,包括小键盘的数字键if('0'<=LOWORD(wParam) && LOWORD(wParam)<='9'){tagResponseStatus=true;}//当按下加、减、乘、除按键时响应if(LOWORD(wParam)==43 || LOWORD(wParam)==45 || LOWORD(wParam)==42 || LOWORD(wParam)==47){tagResponseStatus=true;}//当按下左括号、右括号、小数点键if(LOWORD(wParam)==40 || LOWORD(wParam)==41 || LOWORD(wParam)==46){tagResponseStatus=true;}//对设置的按键命令进行响应if(tagResponseStatus==true){char szCommand[1024]="";sprintf(szCommand,"%c",LOWORD(wParam));OnExcuteString(hWnd,szCommand);}//当按下回车、ESC、等号、BACKSPACE键执行相应的指令if(LOWORD(wParam)==13){OnExcuteString(hWnd,"RETURN");}if(LOWORD(wParam)==27){OnExcuteString(hWnd,"ESC");}if(LOWORD(wParam)==61){OnExcuteString(hWnd,"=");}if(LOWORD(wParam)==8){OnExcuteString(hWnd,"DEL");}}void	Calculator::OnKeyDown(HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam)
{//键盘按下DEL键执行清空显示控件的指令if(LOWORD(wParam)==46){OnExcuteString(hWnd,"C");}}//屏幕显示内容void	Calculator::OnPaint(HWND hWnd,HDC hDC)
{//显示背景颜色if(hBackGroundBitmap!=NULL){BITMAP BM;RECT tempClientRect;GetClientRect(hWnd,&tempClientRect);HDC hTemDC=::CreateCompatibleDC(hDC);SelectObject(hTemDC,hBackGroundBitmap);GetObject(hBackGroundBitmap,sizeof(BITMAP),&BM);BitBlt(hDC,0,0,tempClientRect.right,tempClientRect.bottom,hTemDC,(BM.bmWidth-tempClientRect.right)/2,(BM.bmHeight-tempClientRect.bottom)/2,SRCCOPY);DeleteDC(hTemDC);}//调试信息,防止内存泄露if(!true){char szTemp[1024]="";sprintf(szTemp,"st_StackNodeNum:%d",st_StackNodeNum);TextOut(hDC,10,120,szTemp,strlen(szTemp));}}void	Calculator::OnExcuteString(HWND hWnd,char *szCommand)
{//每次输入时重置错误信息tagError=false;strcpy(szErrorMessage,"");//如果当前需要添加的是数字if(IsNumber(szCommand)==true){if(strlen(szExpression)>0 && strlen(szExpression)<500){//获取表达式的最后一个字符并转换为字符串char szEndChar[10]="";sprintf(szEndChar,"%c",szExpression[strlen(szExpression)-1]);//如果末尾不为右括号则直接添加if(strcmp(szEndChar,")")!=0){strcat(szExpression,szCommand);}}else{strcat(szExpression,szCommand);}}//如果当前需要添加的是运算符if(IsOperator(szCommand)==true){if(strlen(szExpression)>0 && strlen(szExpression)<500){//获取表达式的最后一个字符并转换为字符串char szEndChar[10]="";sprintf(szEndChar,"%c",szExpression[strlen(szExpression)-1]);//判断字符串结尾字符是否为数字if(IsNumber(szEndChar)==true || strcmp(szEndChar,")")==0 || strcmp(szEndChar,".")==0){strcat(szExpression,szCommand);}//如果末尾字符为运算符,删除用新的运算符替换旧的运算符else if(IsOperator(szEndChar)==true){//在新的字符串中进行操作char szNewExpression[1024]="";//拷贝到新的字符串进行操作strcpy(szNewExpression,szExpression);//删除一个字符szNewExpression[strlen(szNewExpression)-1]='\0';//添加新的运算符号strcat(szNewExpression,szCommand);//拷贝新的字符串到原算式表达式字符串strcpy(szExpression,szNewExpression);}}}//左括号的输入if(strcmp(szCommand,"(")==0){if(strlen(szExpression)<500){//获取表达式的最后一个字符并转换为字符串char szEndChar[10]="";sprintf(szEndChar,"%c",szExpression[strlen(szExpression)-1]);//末尾字符为运算数字运算符的替换,为数字(或其他)的则直接添加if(strlen(szExpression)==0 || IsOperator(szEndChar)==true || strcmp(szEndChar,"(")==0){strcat(szExpression,szCommand);}else{MessageBeep(MB_OK);}}}//右括号的输入if(strcmp(szCommand,")")==0){if(strlen(szExpression)<500){//获取表达式的最后一个字符并转换为字符串char szEndChar[10]="";sprintf(szEndChar,"%c",szExpression[strlen(szExpression)-1]);//判断字符串结尾字符是否为数字if((IsNumber(szEndChar)==true || strcmp(szEndChar,")")==0) && GetCharAmount(szExpression,'(')>GetCharAmount(szExpression,')')){strcat(szExpression,szCommand);}	else{MessageBeep(MB_OK);}	}}//如果前一个字符是数字,则直接添加到算式中if(strcmp(szCommand,".")==0){if(strlen(szExpression)<500){//获取表达式的最后一个字符并转换为字符串char szEndChar[10]="";sprintf(szEndChar,"%c",szExpression[strlen(szExpression)-1]);//判断字符串结尾字符是否为数字if(IsNumber(szEndChar)==true){strcat(szExpression,szCommand);}}}//如果是数字,则直接添加到算式显示控件if(strcmp(szCommand,"C")==0){strcpy(szExpression,"");strcpy(szCheckedExpression,"");strcpy(szResult,"");}//如果是数字,则直接添加到算式显示控件if(strcmp(szCommand,"DEL")==0){if(strlen(szExpression)>0){//获取表达式的最后一个字符并转换为字符串char szEndChar[10]="";sprintf(szEndChar,"%c",szExpression[strlen(szExpression)-1]);//在新的字符串中进行操作char szNewExpression[1024]="";//拷贝到新的字符串进行操作strcpy(szNewExpression,szExpression);//删除一个字符szNewExpression[strlen(szNewExpression)-1]='\0';//拷贝新的字符串到原算式表达式字符串strcpy(szExpression,szNewExpression);}else{MessageBeep(MB_OK);}}//根据用户输入的字符串表达式智能纠错后计算结果GetResultValueByString();//当按下等于号对结果进行交换保存if(strcmp(szCommand,"=")==0){if(tagError!=true){//将结果保存到算式表达式字符串,并重置其他字符串strcpy(szExpression,szResult);strcpy(szCheckedExpression,"");strcpy(szResult,"");}else{MessageBeep(MB_OK);}}//更新显示“ 字符串显示框”SetWindowText(GetDlgItem(hWnd,51),szExpression);//更新显示校验后的“ 字符串显示框”SetWindowText(GetDlgItem(hWnd,52),szCheckedExpression);//更新显示“ 字符串显示框”SetWindowText(GetDlgItem(hWnd,53),szResult);//更新界面//InvalidateRect(hWnd,NULL,false);}void	Calculator::CalValue(Stack<double> *&ptNumStack,Stack<char> *&ptOperatorStack)
{double NumberLeft, NumberRight, NumberResult;char Operator;//将栈顶的两个数字和一个操作符进行出栈操作PopStack(ptNumStack, NumberRight);PopStack(ptNumStack, NumberLeft);PopStack(ptOperatorStack, Operator);//记录除数为零的情况if(NumberRight==0 || NumberLeft==0){tagError=true;strcpy(szErrorMessage,"错误:除数不能为零");}//对出栈的两个数字和一个操作符进行计算if (Operator == '+')NumberResult = NumberLeft + NumberRight;if (Operator == '-')NumberResult = NumberLeft - NumberRight;if (Operator == '*')NumberResult = NumberLeft * NumberRight;if (Operator == '/')NumberResult = NumberLeft / NumberRight;//将计算后的结果继续押入栈中PushStack(ptNumStack, NumberResult);}//逆波兰算法实现double	Calculator::Polish(char *String, int len)
{Stack<double> *ptNumStack;Stack<char> *ptOperatorStack;//初始栈,最主要产生一个默认的节点InitStack(ptNumStack);InitStack(ptOperatorStack);//逐字符读取字符串的游标位置int index = 0;//逐字符读取字符串while(!IsStackEmpty(ptOperatorStack) || index<len){//当前游标位置小于字符串长度时,逐个获取数字和运算符号并进行运算;否则做收尾工作if(index<len){//如果当前游标位置为数字,则说明这里是我们需要读取数字的开始位置if((String[index] >= '0' && String[index] <= '9') || String[index]=='.'){//将此后的数字区域读取到临时字符串char szTempNum[100]="";int iPos=0;//循环取得数字,当遇到第一个不是数字或小数点的字符for(int i=index;i<len;i++){if((String[i] >= '0' && String[i] <= '9') || String[i]=='.'){szTempNum[iPos++]=String[i];index++;}else{break;}}szTempNum[iPos]='\0';//获取到我们需要的数字,并将数字保存到栈中double tempNumber=atof(szTempNum);PushStack(ptNumStack, tempNumber);}else {//如果当前字符串为运算符,则根据运行符的种类进行判断if (String[index] == '(' || (GetStackTopValue(ptOperatorStack) == '(' && String[index] != ')') || IsStackEmpty(ptOperatorStack)){//遇到以上情况,则直接将运行符保存的符号栈中PushStack(ptOperatorStack, String[index++]);}else if (String[index] == '+' || String[index] == '-'){//如果遇到加、减号,就把此前已入栈的算式进行计算,并将结果结果和符号重新入栈while (GetStackTopValue(ptOperatorStack) != '(' && !IsStackEmpty(ptOperatorStack)){CalValue(ptNumStack, ptOperatorStack);}PushStack(ptOperatorStack, String[index++]);}else if (String[index] == '*' || String[index] == '/')				{//如果遇到乘、除号,则把之前所有乘、除相关的算式进行计算,并将结果结果和符号重新入栈while (GetStackTopValue(ptOperatorStack) == '*' || GetStackTopValue(ptOperatorStack) == '/'){CalValue(ptNumStack, ptOperatorStack);}PushStack(ptOperatorStack, String[index++]);}else if (String[index] == ')'){//当遇到有括号,则把所有括号对内的算式计算完毕,右括号无需入栈while(GetStackTopValue(ptOperatorStack) != '(') {//当栈为空时无需进行计算跳出循环if(IsStackEmpty(ptOperatorStack))break;CalValue(ptNumStack, ptOperatorStack);}//当计算完所有括号内容的算式,弹出对应的左括号char tempBracket;PopStack(ptOperatorStack, tempBracket);index++;}}}else{//遍历完字符串所有字符后,只需对还未空的运算符栈进行逐步计算CalValue(ptNumStack, ptOperatorStack);}}//最后栈顶(根节点的下一个节点)的数据就是表达式的结果double NumberValue=0;PopStack(ptNumStack,NumberValue);//在删除所有子节点后销毁栈的根节点DestroyStack(ptNumStack);DestroyStack(ptOperatorStack);//返回计算的结果return NumberValue;}//根据字符串计算结果double	Calculator::GetResultValueByString()
{//校验用户的输入,进行自动纠错处理strcpy(szCheckedExpression,szExpression);//对客户输入的算式进行自动纠错处理if(strlen(szCheckedExpression)!=0){//自动去除末尾的非数字符号for(int i=strlen(szCheckedExpression)-1;i>=0;i--){//获取表达式的最后一个字符并转换为字符串char szEndChar[10]="";sprintf(szEndChar,"%c",szCheckedExpression[strlen(szCheckedExpression)-1]);//智能删除用户算式表达式的末尾运算符号和左括号if(IsNumber(szEndChar)==false && strcmp(szEndChar,")")!=0 && strcmp(szEndChar,".")!=0){szCheckedExpression[i]='\0';}else{break;}}//自动添加用户应加未加的右括号int iTempBracketNum=GetCharAmount(szCheckedExpression,'(')-GetCharAmount(szCheckedExpression,')');//自动添加用户应加未加的右括号for(int j=0;j<iTempBracketNum;j++){strcat(szCheckedExpression,")");}//将纠错后的字符串进行计算处理if(strlen(szCheckedExpression)!=0){//根据纠错后的算式字符串计算结果ResultDate=Polish(szCheckedExpression,strlen(szCheckedExpression));//显示出结果sprintf(szResult,"%0.10f",ResultDate);//删除小数部分末尾的零TrimBackZero(szResult);//如果出现错误,则只显示错误信息if(tagError==true){strcpy(szResult,szErrorMessage);}}}else{//用户自定义字符串为空时,重置结果字符串strcpy(szCheckedExpression,"");strcpy(szResult,"");}return 0;}//消息处理模块LRESULT CALLBACK WndProc(HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam)
{HDC hDC=NULL;switch(message){case WM_CREATE://初始化并创建按键、算式显示框和结果显示框Calculators.Initialize(hWnd);return 0;case WM_PAINT:PAINTSTRUCT PS;	hDC=BeginPaint(hWnd,&PS);//显示屏幕内容Calculators.OnPaint(hWnd,hDC);ReleaseDC(hWnd,hDC);return 0;case WM_COMMAND://根据消息执行计算器的操作Calculators.OnCommand(hWnd,message,wParam,lParam);return 0;case WM_CHAR://根据消息执行计算器的操作Calculators.OnChar(hWnd,message,wParam,lParam);return 0;case WM_KEYDOWN://根据消息执行计算器的操作Calculators.OnKeyDown(hWnd,message,wParam,lParam);return 0;case WM_DESTROY:PostQuitMessage(0);return 0;}return DefWindowProc(hWnd,message,wParam,lParam);}//
//Calculator计算器经典版
//作者:zhooyu
//2024.12.7
//CSDN主页地址:https://blog.csdn.net/zhooyu
//CSDN文章地址:https://blog.csdn.net/zhooyu/article/details/144202897
////主函数int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,PSTR szCmdLine,int iCmdShow)
{MSG		msg;HWND	hWnd;CHAR	szAppName[]="Calculator";//设置程序的样式WNDCLASS		WC;WC.style		= CS_HREDRAW|CS_VREDRAW;WC.lpfnWndProc		= WndProc;WC.cbClsExtra		= 0;WC.cbWndExtra		= 0;WC.hInstance		= hInstance;WC.hIcon		= LoadIcon(hInstance,IDI_APPLICATION);WC.hCursor		= LoadCursor(hInstance,IDC_ARROW);WC.hbrBackground	= (HBRUSH)GetStockObject(GRAY_BRUSH);WC.lpszMenuName		= NULL;WC.lpszClassName	= szAppName;if(!RegisterClass(&WC)){return 0;}//创建窗口hWnd=CreateWindow(szAppName,szAppName,WS_OVERLAPPEDWINDOW&~WS_THICKFRAME&~WS_MAXIMIZEBOX,CW_USEDEFAULT,CW_USEDEFAULT,295,390,NULL,NULL,hInstance,NULL);//显示更新窗口ShowWindow(hWnd,iCmdShow);UpdateWindow(hWnd);//消息循环while(GetMessage(&msg,NULL,0,0)){TranslateMessage(&msg);DispatchMessage(&msg);//调试信息if(msg.message==WM_CHAR || msg.message==WM_KEYDOWN){//调试信息if(!true){char szTemp[1024]="";sprintf(szTemp,"%d,%d",msg.hwnd,hWnd);MessageBox(NULL,szTemp,"",MB_OK);}//确保父窗口收到按键消息if(msg.hwnd!=hWnd){SendMessage(hWnd,msg.message,msg.wParam,msg.lParam);}}}return msg.wParam;}

6、源码下载

该源码可以在VS2010和VC6.0中无差异运行,因此就上传了两个版本的源码,方便运行。

6.1、VS2010源码下载

CSDN下载地址:Calculator20241207-15-vs2010.rar

6.2、VC6.0源码下载

CSDN下载地址:Calculator20241207-15-vc6.0.rar

相关文章:

C++实现一个经典计算器(逆波兰算法)附源码

1、本篇要实现的内容 最近&#xff0c;大家讨论计算器的实现比较热&#xff0c;今天我也来用C和Visual Studio实现一个计算器的小程序。这里使用逆波兰算法&#xff0c;能够根据当前用户输入的算式表达式字符串&#xff0c;计算出所要的结果&#xff0c;算式字符串可以包括加、…...

Python知识分享第二十二天-数据结构入门

数据结构 “”" 基础概念: 程序 数据结构 算法 数据结构 存储和组织数据的方式. 算法 解决问题的思维, 思路, 方式. 算法的特性:独立性: 算法 思维, 是解决问题的思路和方式, 不依赖语言.5大特性: 有输入, 有输出, 有穷性, 确定性, 可行性.问: 如何衡量算法的优劣?…...

【WRF理论第十三期】详细介绍 Registry 的作用、结构和内容

目录 1. Introduction&#xff1a;介绍 Registry 的作用和功能。2. Registry Contents&#xff1a;详细描述 Registry 的结构和内容&#xff0c;包括各个部分的条目类型。2.1. DIMSPEC ENTRIES&#xff08;维度规格条目&#xff09;2.2. STATE ENTRIES&#xff08;状态变量条目…...

Android启动优化指南

文章目录 前言一、启动分类与优化目标1、冷启动1.1 优化思路1.2 延迟初始化与按需加载1.3 并行加载与异步执行1.4 资源优化与懒加载1.5 内存优化与垃圾回收控制 2. 温启动2.1 优化应用的生命周期管理2.2 数据缓存与懒加载2.3 延迟渲染与视图优化 3. 热启动3.1 保持应用的状态3.…...

【ETCD】【源码阅读】configureClientListeners () 函数解析

逐步解析 configureClientListeners 函数 configureClientListeners 是 ETCD 的一个重要函数&#xff0c;用于配置客户端通信的监听器&#xff08;Client Listeners&#xff09;。这些监听器主要负责处理外部客户端与 ETCD 服务之间的通信&#xff0c;包括 HTTP 和 gRPC 请求。…...

IO进程学习笔记

man手册 普通命令。系统调用的函数。库函数。特殊文件。文件格式。游戏。附加的一些变量 IO介绍 I&#xff1a;input 输入 O&#xff1a;output 输出 对文件的输入和输出 输入-》写文件&#xff0c;将文件中的内容写到内存中去 输出-》读文件&#xff0c;将内存中的内容读取到文…...

智能手机回暖:华为点火,小米荣耀OV拱火

进入11月中下旬&#xff0c;智能手机圈再度热闹起来。包括华为、小米、OPPO、vivo等诸多手机厂商&#xff0c;都在陆续预热发布新机&#xff0c;其中就包括华为Mate 70、小米Redmi K80、vivo的S20&#xff0c;IQOO Neo10等热门新机&#xff0c;这些热门新机的集中上市迅速吸引了…...

Sqoop导入数据(mysql---->>hive)

目录 数据传输流程脚本报错和异常说明1. Caused by: java.lang.ClassNotFoundException: org.apache.hadoop.hive.conf.HiveConf2. 数据导入hive后显示NULL 数据传输流程 mysql---->>hdfs---->>hive 数据从mysql表中取出&#xff0c;放到hdfs上&#xff08;由targ…...

实验3-实时数据流处理-Flink

1.前期准备 &#xff08;1&#xff09;Flink基础环境安装 参考文章&#xff1a; 利用docker-compose来搭建flink集群-CSDN博客 显示为这样就成功了 &#xff08;2&#xff09;把docker&#xff0c;docker-compose&#xff0c;kafka集群安装配置好 参考文章&#xff1a; …...

深度学习实验十四 循环神经网络(1)——测试简单循环网络的记忆能力

目录 一、数据集构建 1.1数据集的构建函数 1.2加载数据集并划分 1.3 构建Dataset类 二、模型构建 2.1嵌入层 2.2SRN层 2.3模型汇总 三、模型训练 3.1 训练指定长度的数字预测模型 3.2 损失曲线展示 四、模型评价 五、修改 附完整可运行代码 实验大体步骤&#x…...

k8s部署odoo18(kubeshpere面板)

Postgresql部署 链接: kubesphere搭建 postgres15 因为我的是在另一台服务器使用kubesphere进行部署的&#xff0c;如果有和我一样情况的&#xff0c;可以参考上面的文档部署postgreasql。 注意事项&#xff1a; 因为odoo不允许使用postgresql的默认用户&#xff0c;也就是po…...

【模型对比】ChatGPT vs Kimi vs 文心一言那个更好用?数据详细解析,找出最适合你的AI辅助工具!

在这个人工智能迅猛发展的时代&#xff0c;AI聊天助手已经深入我们的工作与生活。你是否曾在选择使用ChatGPT、Kimi或是百度的文心一言时感到一头雾水&#xff1f;每款AI都有其独特的魅力与优势&#xff0c;那么&#xff0c;究竟哪一款AI聊天助手最适合你呢&#xff1f;本文将带…...

Java——容器(单例集合)(上)

一 容器介绍 容器&#xff0c;是用来容纳物体、管理物体。生活中,我们会用到各种各样的容器。如锅碗瓢盆、箱子和包等 程序中的“容器”也有类似的功能&#xff0c;用来容纳和管理数据。比如&#xff0c;如下新闻网站的新闻列表、教育网站的课程列表就是用“容器”来管理 视频…...

如何配置Github并在本地提交代码

前提: 可以流畅访问github, 需要一些上网技巧, 这就自行处理了 申请一个github账号 Github官网地址 首先就是邮箱注册啦, github没有对邮箱的限制, 只要是能收邮件的就ok, qq邮箱, 163等都可以使用. 然后和普通注册账号一样, 一路填写需要的信息, 验证邮箱即可. 如何新增代…...

工作bug,keil5编译器,理解int 类型函数返回值问题,详解!!!

编写不易&#xff0c;禁止搬运&#xff0c;仅供学习&#xff0c;感谢理解 问题现象 下面是一个在keil5里面写的一个&#xff0c;int类型的返回值函数&#xff0c;这个函数里面&#xff0c;只有if else if else这三个判断条件语句&#xff0c;正常来说任何情况下&#xff0c;…...

简明速通Java接口

前言 欢迎来到我的博客 个人主页:北岭敲键盘的荒漠猫-CSDN博客 本文从代码层面直接整理Java接口 让老油子们无需再理解繁杂的概念了。 Java接口在代码层面是做什么的 说白了老铁&#xff0c;Java的接口就是一个类&#xff0c;这个类中只能声明属性和方法&#xff0c;属性需要…...

MVC基础——市场管理系统(二)

文章目录 项目地址三、Produtcts的CRUD3.1 Products列表的展示页面(Read)3.1.1 给Product的Model里添加Category的属性3.1.2 View视图里展示Product List3.2 增加Product数据(Add)3.2.1 创建ViewModel用来组合多个Model3.2.2 在_ViewImposts里引入ViewModels3.2.3 添加Add的…...

java------------常用API preiod duration 计算时间差

1&#xff0c;preiod 如果末天数比初天数小&#xff0c;需要进一位 package API;import java.time.LocalDate; import java.time.Period;public class preiod {public static void main(String[] args) {// 计算时间差// LocalDate获取对象其中的一个方法LocalDate d1 LocalD…...

使用 FAISS 进行高效相似性搜索:从文本检索到动态数据处理

在现代数据科学和人工智能应用中&#xff0c;处理大量高维数据并从中找到相似项是一个常见任务。无论是在推荐系统、搜索引擎&#xff0c;还是在自然语言处理应用中&#xff0c;如何高效地进行相似性搜索&#xff08;Similarity Search&#xff09;一直是一个挑战。为了解决这个…...

执行“go mod tidy”遇到“misbehavior”错误

执行“go mod tidy”报错下错误&#xff0c;执行“go clean -modcache”和删除“go env GOMODCACHE”指定目录均无效&#xff1a; SECURITY ERROR go.sum database server misbehavior detected!old database:go.sum database tree3397826xyyhzdyAOat5li/EXx/MK1gONQf3LAGqArh…...

深入详解人工智能机器学习:强化学习

目录 强化学习概述 强化学习的基本概念 定义 关键组件 强化学习过程 常用算法 应用示例 示例代码 代码解释 应用场景 强化学习核心概念和底层原理 核心概念 底层原理 总结 强化学习概述 强化学习&#xff08;Reinforcement Learning, RL&#xff09;是机器学习中的…...

力扣打卡11:合并区间(比较器内联,引用传参的优化)

链接&#xff1a;56. 合并区间 - 力扣&#xff08;LeetCode&#xff09; 这道题可以用贪心。 首先将intervals的left&#xff08;intervals[i][0]&#xff09;排序。 然后拿出第一个区间&#xff0c;比较后面相邻的区间&#xff1a; 当前right<后left&#xff0c;表示下一…...

《 bilibili-起步级 用户模块接口文档 经验分享 ~》

bilibili - 用户模块接口文档 - 经验分享 ~ 数据库er关系图 : 迅速跳转链接 枚举码实体类 : 迅速跳转链接 使用apifox.json格式导入接口文档 步骤 登录Apifox。新建文件, 将代码粘贴到该文件, 并更改后缀为 .apifox.json进入项目&#xff0c;点击“导入”。选择“Apifox”格式…...

AES 与 SM4 加密算法:深度解析与对比

&#x1f9d1; 博主简介&#xff1a;CSDN博客专家&#xff0c;历代文学网&#xff08;PC端可以访问&#xff1a;https://literature.sinhy.com/#/literature?__c1000&#xff0c;移动端可微信小程序搜索“历代文学”&#xff09;总架构师&#xff0c;15年工作经验&#xff0c;…...

启保停电路如何接到PLC

传感器&#xff1a;NPN :棕&#xff1a;正 蓝&#xff1a;负 黑&#xff1a;信号 1M——>24V PNP&#xff1a;1M——>0V...

HTTP multipart/form-data 请求

序言 最近在写项目的过程中有一个需求是利用 HTTP 协议传输图片和视频&#xff0c;经过查询方法相应的方法发现使用 multipart/form-data 的方式&#xff0c;这是最常见处理二进制文件的表单编码类型。  学习了一下午&#xff0c;现在总结一下使用的方法和相关的知识点&#x…...

配置服务器的免密登录

在服务器中配置别名和免密登录 如果没有生成过公钥和密钥 ssh-keygen然后就生成了公钥和密钥&#xff0c;下一步进入.ssh文件夹 cd .ssh/可以看到文件夹中会多出来三个文件 id_rsa&#xff1a;密钥id_rsa.pub&#xff1a;公钥known_hosts&#xff1a;A通过ssh首次连接到B&am…...

普通遥控电动遮阳雨棚怎么接入米家并用苹果手机Siri控制

环境&#xff1a; 遥控电动遮阳雨棚 无线射频拷贝器 米家APP 问题描述&#xff1a; 普通遥控电动遮阳雨棚怎么接入米家并用苹果手机Siri控制 解决方案&#xff1a; 1.先看看遥控器射频参数,有些在里面板子上&#xff0c;要拆开才能看到&#xff0c;我这是433的 2.到网店…...

两种不同简缩极化的六个方程

方程1 (3*A*(b - a*1i 1) - A*((c d*1i)*(f1 f2*1i)*1i - (c d*1i)^2))*(a - b*1i)*1i 3*A*(b - a*1i 1) 2*(A*(c f2 d*1i - f1*1i) A*(c d*1i - (a b*1i)*(c d*1i)*1i))*(c - d*1i) (A*(c f2 d*1i - f1*1i) A*(c d*1i - (a b*1i)*(c d*1i)*1i))*(f1 - f2…...

环形缓冲区(Ring Buffer):概念、功能、使用场景与实现

一、概念 环形缓冲区&#xff08;Ring Buffer&#xff09;&#xff0c;又称循环缓冲区&#xff0c;是一种用于数据缓冲的数据结构。其核心思想是将缓冲区视为一个环形结构&#xff0c;当数据写入到缓冲区的末尾时&#xff0c;会自动回绕到缓冲区的开头继续写入&#xff0c;形成…...