小型网站建设价格,wordpress文章postid,校园网站怎么建设,兰州网站建设技能论文1、本篇要实现的内容
最近#xff0c;大家讨论计算器的实现比较热#xff0c;今天我也来用C和Visual Studio实现一个计算器的小程序。这里使用逆波兰算法#xff0c;能够根据当前用户输入的算式表达式字符串#xff0c;计算出所要的结果#xff0c;算式字符串可以包括加、…1、本篇要实现的内容
最近大家讨论计算器的实现比较热今天我也来用C和Visual Studio实现一个计算器的小程序。这里使用逆波兰算法能够根据当前用户输入的算式表达式字符串计算出所要的结果算式字符串可以包括加、减、乘、除和括号支持整数、小数鼠标和键盘均可操作实现了一个较为经典的计算器功能。后期如果有时间我们再实现一些更多的计算器功能。本篇实现的效果如下 2、设计目标
我们今天想制作一个计算器需要基本上能达到日常使用的需求。首先它得有可操作的图形窗口界面它要能够满足我们一些基本的计算需求如整数和小数的加、减、乘、除顺便再把括号功能附加上。同时我们在设计的时候还允许用户输入算式表达式字符串程序能根据用户输入算式表达式字符串经过一些智能纠错后对纠错后的算式表达式进行实时计算并最终显示出结果。
2.1 、运行环境
操作系统Windows10操作系统 编译环境Microsoft Visual Studio 2010VC6.0也可以直接编译运行 其它事项源代码仅仅包括一个cpp源文件新建项目可直接编译运行无需在资源编辑器中额外创建按键、显示框等控件资源。
2.2、实现图形化界面
首先计算器要方便使用我们必须为它创建一个友好的图形界面。我们首先为他创建一个应用窗口并为窗口添加相应的控件。控间最主要包括两大部分一部分是用于用户输入的响应按键另一部分是用于反馈用户输入和计算结果的显示控件。为了简化项目我们这里采用系统CreateWindow函数创建的按键BUTTON控件和STATIC控件分别来响应用户输入和输出。图形的区域分布如下
2.3、实现字符串算式自动识别计算
通过字符串算式自动识别数学表达式有两个优点。第一个优点可以方便用户随时校对自己输入算式表达式的正确性。在计算时我不仅仅需要看到的是计算后的得数有时候我还需要看到我们已经输入的算术表达式方便我校对输入的式子是否正确如发现错误还可以及时修改。第二个优点字符串算式表达式可以考虑到算式计算的优先级。在普通没有字符串表达式的计算器中我们每输入一个算术符号和数字就必须要计算出这一步的结果。如此循环操作再往下继续输入运算符号和数字屏幕只显示当前的结果。那么这样就势必无法考虑到加减乘除运算规则只能根据用户输入算式的先后顺序计算更没有办法考虑到括号的优先运算。那么字符串算数表达式就可以完美解决这个问题这里还要用到逆波兰算法。 在这个算式中我们需要先计算3*1339的乘法在计算123951的加法。
2.4、支持加、减、乘、除和括号
由于使用了逆波兰算法这里运算我们支持加减乘除还添加了对括号的支持。我们采用了字符串算式格式我们可以方便的对加减乘除和括号的运算规则进行支持。因为在日常的运算中如果拿着计算器还需要自己去考虑一个算式的运算顺序的话会是一个很糟糕的体验。 2.5、实时更新运算结果
我们在我们在制作计算机前期构想的时候借鉴了手机上自带计算器功能的一些创意用户每输入一个字符都会更新并影响到最终结果。在使用计算器的时候当用户每输入一个数字或者符号时计算器都会根据当前已经输入的算式表达式进行智能分析预估出用户可能需要的结果随即实时计算出结果并显示。 2.6、智能运算符号校验
我们是采用对字符串进行逆波兰法计算并且是实时每输入一个数字或字符都会影响到结果计算因此对算式字符串的规范性检测要求较高。但是我们日常在输入字符串表达式的时候难免会存在一些手误比如说连续输入两个乘号等等那么这类的错误操作就需要我们用用户输入逻辑去加以规范或限制。同时还有用户在输入括号时表达式中的左右括号数量不一致等问题将会导致计算出现错误。我们这里通过输入逻辑检测解决了用户输入表达式的规范性。
2.7、错误判断提示
在遇到除数为零的特殊情况时我们需要在结果中输出错误提示否则计算会出现意外。如下图 2.8、支持整数、小数运算
这里我们要双精度数据类型进行计算确保计算的准确性。对小数的计算是我们日常生活中不可少的部分计算器并没有增加对小数的支持。本次在程序设计的开始就考虑到了这一点。这里包括对有限小数的计算包括对循环小数的计算以及无限循环小数结果的显示逻辑。
2.9、使用逆波兰算法计算数学表达式 一. 波兰式前缀表达式 波兰逻辑学家J.Lukasiewicz于1929年提出的表示表达式的一种方式即二元运算符至于运算数之前的一种表达方式。 二.中缀表达式 普通的表示表达式的一种方法将二元运算符置于运算数中间也是大多数情况下使用的一种方法。 三.逆波兰式后缀表达式 与波兰式相反是二元运算符置于运算数之后的一种表达方式。每一运算符都置于其运算对象之后故称为后缀表示。 三种表达式的形象实例如下 逆波兰式的应用——算术表达式求值 逆波兰式也称逆波兰记法Reverse Polish Notation。在数据结构中使用栈的概念完成表达式的求值操作在计算机系统处理表达式的计算过程中将中缀表达式转换为后缀表达式的形式进行解析转换并实施计算这就是逆波兰算法的应用。 具体实现方法大致为 设两个栈操作数栈和运算符栈操作数依次入操作数栈运算符入栈前与运算符的栈顶运算符比较优先级优先级高于栈顶运算符压入栈读入下一个符号优先级低于栈顶运算符栈顶运算符出栈操作数栈退出两个操作数进行运算结果压入操作数栈优先级相等左右括号相遇栈顶运算符出栈即可后缀表达式读完栈顶为运算结果。 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_StackNodeNum0;//链栈templatetypename Typestruct Stack
{Type num;StackType* ptNext;};//初始化栈templatetypename Typevoid InitStack(StackType* Node)
{Node (StackType*)malloc(sizeof(StackType));Node-ptNext NULL;st_StackNodeNum;}//头插法入栈templatetypename Typevoid PushStack(StackType* Node, Type value)
{StackType* pt (StackType*)malloc(sizeof(StackType));pt-num value;pt-ptNext Node-ptNext;Node-ptNext pt;st_StackNodeNum;}//头插法出栈templatetypename Typevoid PopStack(StackType* Node, Type value)
{StackType* pt Node-ptNext;value pt-num;Node-ptNext pt-ptNext;delete pt;st_StackNodeNum--;}//头插法出栈templatetypename Typevoid DestroyStack(StackType* Node)
{if(Node-ptNext NULL){delete Node;NodeNULL;st_StackNodeNum--;}}//判断栈是否为空除去没有存数据的首个节点外templatetypename Typebool IsStackEmpty(StackType* Node)
{return Node-ptNext NULL;}//获取栈顶部节点的数据templatetypename TypeType GetStackTopValue(StackType* Node)
{if(Node-ptNext !NULL){return Node-ptNext-num;}else{return 0;}}4.2、字符串操作函数
在字符表达式的输入和处理过程中会遇到一些必须的字符处理函数我们在这里定义。 //省略掉数字的小数点后末尾多余的零void TrimBackZero(char *szString)
{//标记小数点的位置int iDotPos-1;//先找到小数点的位置for(int i0;ilstrlen(szString);i){if(szString[i].){iDotPosi;break;}}//寻找末尾多余的零for(int jlstrlen(szString)-1;jiDotPos;j--){if(szString[j]. || szString[j]0){szString[j]\0;}else{break;}}}//获取字符串中某个字符的个数int GetCharAmount(char *szString,char sign)
{int iAmount0;for(int i0;ilstrlen(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(Stackdouble *ptNumStack,Stackchar *ptOperatorStack);//逆波兰算法实现double Polish(char *String, int len);};//自定义计算器类实例Calculator Calculators;Calculator::Calculator()
{hCtlFontNULL;strcpy(szExpression,);strcpy(szCheckedExpression,);strcpy(szResult,);ResultDate0;tagErrorfalse;strcpy(szErrorMessage,);hBackGroundBitmapNULL;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 hCtlFontCreateFont(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 i0;iBUTTONMAXNUM;i){//设置按键的宽和高int w60,h35,gap10;//设置按键的坐标位置int x10(i%4)*(wgap),ytempClientRect.bottom-h-gap-(i/4)*(hgap);//创建按键子控件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 tagResponseStatusfalse;//当用户按下数字键包括小键盘的数字键if(0LOWORD(wParam) LOWORD(wParam)9){tagResponseStatustrue;}//当按下加、减、乘、除按键时响应if(LOWORD(wParam)43 || LOWORD(wParam)45 || LOWORD(wParam)42 || LOWORD(wParam)47){tagResponseStatustrue;}//当按下左括号、右括号、小数点键if(LOWORD(wParam)40 || LOWORD(wParam)41 || LOWORD(wParam)46){tagResponseStatustrue;}//对设置的按键命令进行响应if(tagResponseStatustrue){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)
{//每次输入时重置错误信息tagErrorfalse;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(Stackdouble *ptNumStack,Stackchar *ptOperatorStack)
{double NumberLeft, NumberRight, NumberResult;char Operator;//将栈顶的两个数字和一个操作符进行出栈操作PopStack(ptNumStack, NumberRight);PopStack(ptNumStack, NumberLeft);PopStack(ptOperatorStack, Operator);//记录除数为零的情况if(NumberRight0 || NumberLeft0){tagErrortrue;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)
{Stackdouble *ptNumStack;Stackchar *ptOperatorStack;//初始栈最主要产生一个默认的节点InitStack(ptNumStack);InitStack(ptOperatorStack);//逐字符读取字符串的游标位置int index 0;//逐字符读取字符串while(!IsStackEmpty(ptOperatorStack) || indexlen){//当前游标位置小于字符串长度时逐个获取数字和运算符号并进行运算否则做收尾工作if(indexlen){//如果当前游标位置为数字则说明这里是我们需要读取数字的开始位置if((String[index] 0 String[index] 9) || String[index].){//将此后的数字区域读取到临时字符串char szTempNum[100];int iPos0;//循环取得数字当遇到第一个不是数字或小数点的字符for(int iindex;ilen;i){if((String[i] 0 String[i] 9) || String[i].){szTempNum[iPos]String[i];index;}else{break;}}szTempNum[iPos]\0;//获取到我们需要的数字并将数字保存到栈中double tempNumberatof(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 NumberValue0;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 istrlen(szCheckedExpression)-1;i0;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 iTempBracketNumGetCharAmount(szCheckedExpression,()-GetCharAmount(szCheckedExpression,));//自动添加用户应加未加的右括号for(int j0;jiTempBracketNum;j){strcat(szCheckedExpression,));}//将纠错后的字符串进行计算处理if(strlen(szCheckedExpression)!0){//根据纠错后的算式字符串计算结果ResultDatePolish(szCheckedExpression,strlen(szCheckedExpression));//显示出结果sprintf(szResult,%0.10f,ResultDate);//删除小数部分末尾的零TrimBackZero(szResult);//如果出现错误则只显示错误信息if(tagErrortrue){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 hDCNULL;switch(message){case WM_CREATE://初始化并创建按键、算式显示框和结果显示框Calculators.Initialize(hWnd);return 0;case WM_PAINT:PAINTSTRUCT PS; hDCBeginPaint(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;}//创建窗口hWndCreateWindow(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.messageWM_CHAR || msg.messageWM_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_StackNodeNum0;//链栈templatetypename Typestruct Stack
{Type num;StackType* ptNext;};//初始化栈templatetypename Typevoid InitStack(StackType* Node)
{Node (StackType*)malloc(sizeof(StackType));Node-ptNext NULL;st_StackNodeNum;}//头插法入栈templatetypename Typevoid PushStack(StackType* Node, Type value)
{StackType* pt (StackType*)malloc(sizeof(StackType));pt-num value;pt-ptNext Node-ptNext;Node-ptNext pt;st_StackNodeNum;}//头插法出栈templatetypename Typevoid PopStack(StackType* Node, Type value)
{StackType* pt Node-ptNext;value pt-num;Node-ptNext pt-ptNext;delete pt;st_StackNodeNum--;}//头插法出栈templatetypename Typevoid DestroyStack(StackType* Node)
{if(Node-ptNext NULL){delete Node;NodeNULL;st_StackNodeNum--;}}//判断栈是否为空除去没有存数据的首个节点外templatetypename Typebool IsStackEmpty(StackType* Node)
{return Node-ptNext NULL;}//获取栈顶部节点的数据templatetypename TypeType GetStackTopValue(StackType* Node)
{if(Node-ptNext !NULL){return Node-ptNext-num;}else{return 0;}}//省略掉数字的小数点后末尾多余的零void TrimBackZero(char *szString)
{//标记小数点的位置int iDotPos-1;//先找到小数点的位置for(int i0;ilstrlen(szString);i){if(szString[i].){iDotPosi;break;}}//寻找末尾多余的零for(int jlstrlen(szString)-1;jiDotPos;j--){if(szString[j]. || szString[j]0){szString[j]\0;}else{break;}}}//获取字符串中某个字符的个数int GetCharAmount(char *szString,char sign)
{int iAmount0;for(int i0;ilstrlen(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(Stackdouble *ptNumStack,Stackchar *ptOperatorStack);//逆波兰算法实现double Polish(char *String, int len);};//自定义计算器类实例Calculator Calculators;Calculator::Calculator()
{hCtlFontNULL;strcpy(szExpression,);strcpy(szCheckedExpression,);strcpy(szResult,);ResultDate0;tagErrorfalse;strcpy(szErrorMessage,);hBackGroundBitmapNULL;hBackGroundBitmap(HBITMAP)LoadImage(NULL,bg.bmp,IMAGE_BITMAP,0,0,LR_LOADFROMFILE);}Calculator::~Calculator()
{//删除字体资源DeleteObject(hCtlFont);}void Calculator::Initialize(HWND hWnd)
{//控件字体设置HFONT hCtlFontCreateFont(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 i0;iBUTTONMAXNUM;i){//设置按键的宽和高int w60,h35,gap10;//设置按键的坐标位置int x10(i%4)*(wgap),ytempClientRect.bottom-h-gap-(i/4)*(hgap);//创建按键子控件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 tagResponseStatusfalse;//当用户按下数字键包括小键盘的数字键if(0LOWORD(wParam) LOWORD(wParam)9){tagResponseStatustrue;}//当按下加、减、乘、除按键时响应if(LOWORD(wParam)43 || LOWORD(wParam)45 || LOWORD(wParam)42 || LOWORD(wParam)47){tagResponseStatustrue;}//当按下左括号、右括号、小数点键if(LOWORD(wParam)40 || LOWORD(wParam)41 || LOWORD(wParam)46){tagResponseStatustrue;}//对设置的按键命令进行响应if(tagResponseStatustrue){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)
{//每次输入时重置错误信息tagErrorfalse;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(Stackdouble *ptNumStack,Stackchar *ptOperatorStack)
{double NumberLeft, NumberRight, NumberResult;char Operator;//将栈顶的两个数字和一个操作符进行出栈操作PopStack(ptNumStack, NumberRight);PopStack(ptNumStack, NumberLeft);PopStack(ptOperatorStack, Operator);//记录除数为零的情况if(NumberRight0 || NumberLeft0){tagErrortrue;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)
{Stackdouble *ptNumStack;Stackchar *ptOperatorStack;//初始栈最主要产生一个默认的节点InitStack(ptNumStack);InitStack(ptOperatorStack);//逐字符读取字符串的游标位置int index 0;//逐字符读取字符串while(!IsStackEmpty(ptOperatorStack) || indexlen){//当前游标位置小于字符串长度时逐个获取数字和运算符号并进行运算否则做收尾工作if(indexlen){//如果当前游标位置为数字则说明这里是我们需要读取数字的开始位置if((String[index] 0 String[index] 9) || String[index].){//将此后的数字区域读取到临时字符串char szTempNum[100];int iPos0;//循环取得数字当遇到第一个不是数字或小数点的字符for(int iindex;ilen;i){if((String[i] 0 String[i] 9) || String[i].){szTempNum[iPos]String[i];index;}else{break;}}szTempNum[iPos]\0;//获取到我们需要的数字并将数字保存到栈中double tempNumberatof(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 NumberValue0;PopStack(ptNumStack,NumberValue);//在删除所有子节点后销毁栈的根节点DestroyStack(ptNumStack);DestroyStack(ptOperatorStack);//返回计算的结果return NumberValue;}//根据字符串计算结果double Calculator::GetResultValueByString()
{//校验用户的输入进行自动纠错处理strcpy(szCheckedExpression,szExpression);//对客户输入的算式进行自动纠错处理if(strlen(szCheckedExpression)!0){//自动去除末尾的非数字符号for(int istrlen(szCheckedExpression)-1;i0;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 iTempBracketNumGetCharAmount(szCheckedExpression,()-GetCharAmount(szCheckedExpression,));//自动添加用户应加未加的右括号for(int j0;jiTempBracketNum;j){strcat(szCheckedExpression,));}//将纠错后的字符串进行计算处理if(strlen(szCheckedExpression)!0){//根据纠错后的算式字符串计算结果ResultDatePolish(szCheckedExpression,strlen(szCheckedExpression));//显示出结果sprintf(szResult,%0.10f,ResultDate);//删除小数部分末尾的零TrimBackZero(szResult);//如果出现错误则只显示错误信息if(tagErrortrue){strcpy(szResult,szErrorMessage);}}}else{//用户自定义字符串为空时重置结果字符串strcpy(szCheckedExpression,);strcpy(szResult,);}return 0;}//消息处理模块LRESULT CALLBACK WndProc(HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam)
{HDC hDCNULL;switch(message){case WM_CREATE://初始化并创建按键、算式显示框和结果显示框Calculators.Initialize(hWnd);return 0;case WM_PAINT:PAINTSTRUCT PS; hDCBeginPaint(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;}//创建窗口hWndCreateWindow(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.messageWM_CHAR || msg.messageWM_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