C语言,简单计算器【上】

由于工作需要最近在研究PHP扩展,无可避免的涉及到了C语言。从出了学校以后C语言在实际工作中还没有用到过,所以必须要先进行一点复习工作。个人认为对于熟悉一样东西说最好的方法是上手实践。于是便想起了当时大学的时候老师布置过的一道题目,用C语言实现简单数学表达式的分析和求值,比较遗憾的是当初没能把题目完成。就想着从新试一试,算是补一下当初的作业。

 

还记得当初的思路是,循环C字符串。用链表将不同的计算项存储到链表中。然后在进行循环求值。如果遇到括号就递归调用。回忆并整理了一下当初的思路大致如下。

    1.输入 3+5*(2-6)/2

 

    2.解析为 

        

    3.计算 通过两次循环对不同优先级进行运算得出结果

        第一次对*/进行计算,拿当前节点,和节点的next节点,求值后将值赋给next节点,并删除自身节点

        第二次对+-进行求值,直到遇到end

 

最初也打算按这个思路去实现的,之后发现解析的步骤会比较复杂,涉及到多次的字符串搜索,比对。再加上C本身不支持正则表达式,所以就放弃了当初的思路。找了一些相关的资料后发现了一种更简单也更科学的方法,那就是将输入转换成后缀表达式再进行求值,这后缀表达式究竟是什么呢,请继续看下去。

 

一、后缀表达式

百度百科介绍: 不包含括号,运算符放在两个运算对象的后面,所有的计算按运算符出现的顺序,严格从左向右进行(不再考虑运算符的优先规则,如:(2 + 1) * 3 , 即2 1 + 3 *

 

了解了后缀表达式才知道,原来我们习以为常的数学表达式被称之为中缀表达式。花了点时间研究了一下发现后缀表达式的计算还蛮简单的,也更符合计算机运算。

 

计算方法,从左往右进行计算。取运算符号前两位数字进行运算,运算结果替代运算符以及前两位数字,持续运算到最右边得出结果。

    这么说起来可能比较难理解,我们看几个例子

  1. 最简单的  21+

    1. 计算过程为 2 + 1 = 3

    2. 值为 3

  2. 普通的 325-2*+

    1. 计算过程 从左往右先计算 2-5=-3,-3取代前面的25-之后为 3-32*+,完整的计算步骤如下

    2. 计算2-5=3    计算完之后表达式为 3 -3 2 *+

    3. 计算-3*2=-6  计算完之后表达式为 3 -6 +

    4. 计算3+-6=-3  计算完之后表达式为 -3

    5. 值为 -3

  3. 稍微复杂一点的  21+3*5387-/*-

    1. 计算过程 从左往右先计算 2+1 = 3,4取代前面的21+之后为 33*5387-/*- 完整的计算步骤如下

    2. 计算2+1=3    计算完之后表达式为 3  3 *5 3 8 7 -/*-

    3. 计算3*3=9     计算完之后表达式为 9  5  3  8 7 -/*-

    4. 计算8-7=1     计算完之后表达式为 9  5  3  1 /*-

    5. 计算3/1=3     计算完之后表达式为 9  5 3  *-

    6. 计算5*3=15   计算完之后表达式为 9 15 -

    7. 计算9-15=-6 结果为 -6

    8. 值为 -6

 

 

看完了以上结果,我们会发现每一次参与计算的数字,都是最靠近运算符号的两位数字。然后由运算出来的结果代替参与运算的数字和运算符,直到表达式只剩下一个值,计算完成。

 

 根据这样的规律,程序处理起来就简单了。        

        1.从左往右的循环整个输入。

        2.判断是否是数字,如果是数字就保存起来。如果遇到符号,则把保存的前两个值取出来,计算后把本次计算结果存回去

        3.循环完成之后,剩下的表达式便是计算结果了

    根据如上规则不难发现每次参与计算的两个数字都是最后存进去的,这样一来我们便可以用栈轻松的完成这样一个程序了,下面跟大家简单介绍一下栈。

 

二、栈

    百度百科的解释比较复杂,就不摘抄了。其实栈可以简单的理解为一个存放数据的空间,数据按照后进先出的原则进行存取。对数据的操作有push和pop,分别称之为压入,弹出。

 

    由于比较简单,所以直接用代码实现了一个简单的栈,包含如下四个方法。

    

 

 

    简单的进行测试,输入结果为

    item is : 1.120000

    item is : 2.800000

    2.800000

    1.120000

    实现了预期输出,一个简单的栈就搞定了,接下来就可以利用整个简单的栈来完成求值的函数了。

 

三、后缀表达式求的具体实现

 

    由于栈已经实现了,所以只需要按照后缀表达式求值的逻辑进行运算在配合栈就可以实现整个计算过程了。方法比较简单,用while循环整字符串,在配合switch对数字和运算符做不同的处理就能够完成一个简单的后缀表达式求值函数了。以下是第一版的实现代码

    

 

 

    

    在第一个版本的过程中,遇到一个C语言知识点是 C语言的字符串指针指向的地址是字符串第一个字符的地址。 

    所以当i为0的时候,&str[i] = &str, atof 接收的是一个字符串指针,如果使用 STACKpush(atof(&str[i])),i为0时,会将整个字符串传入进去转换。采用了一个char变量,讲str[i]拷贝出来,然后传入&num,则可以解决这个问题。

 

    其实这段程序里还涉及到一个指针运算的知识点,但是这里的程序里涉及还比较简单易懂,后续还有更难的地方涉及到这个知识点,所以先放到后面再跟大家分享。

 

    对上面的方法进行了测试,输入我们之前分析的三个表达式,得出结果如下

    printf("%f\n", calculate("21+")); //3

    printf("%f\n", calculate("325-2*+")); //-3

    printf("%f\n", calculate("21+3*5387-/*-")); //-6

 

    测试通过,跟之前的计算结果一直。以上便是一个简单的后缀表达式的计算程序了。进行了多几次的测试发现了一个小问题,就是目前无法进行多位数的识别。因为程序没一次都将一位数压入站内了。思考了一下在表达式的每个计算项上加了一个空格符作为数字的区分。变为  21 3 +这种形式,于是动手将代码做了一点小改动

 

    在 switch 中加入了对空格的处理,以及多位数的处理

 

    //如果遇到空格,则重置标志位

   

    这样一来就可以识别多位数了。经测试 

    printf("%f\n", calculate("21 1+")); //22

    printf("%f\n", calculate("2 11+")); //13

    得到了正确的结果,整个简单计算器的第一步计算后缀表达式完成。

    有兴趣的可以测试一下上的代码,如果遇到什么问题可以通过微信公众号反馈给我。

    当然这个程序还有一些有待完善的地方。如表达式合法性检查,对小数的处理,以及对负数的处理等,暂时先预留着后续再跟大家分享如何实现以及会用到的知识点。

    下一篇将为大家介绍简单数学表达式计算的第二步,如何实现将中缀表达式转换为后缀表达式。

  

  欢迎大家关注微信公众号~

 

  

 

posted @ 2017-06-03 17:39  猫猫大侠  阅读(6960)  评论(0编辑  收藏  举报