在《C程序设计伴侣》的8.7.3 向main()函数传递数据这一小节中,我们介绍了如何通过main()函数的参数,向程序传递两个数据并计算其和值的简单加法计算器add.exe。这个程序,好用是好用,就是太简单,还停留在幼儿园大班的水平,只能计算两位数的加法。我们现在基本都已经是大学生了,如果还是用这个简陋的加法计算器去向面试官展示我们的编程能力,肯定会遭到他们的笑话。
在看完《C程序设计伴侣》后,我们的编程能力已经今非昔比了。自然,我们也可以利用从这本书中学到的知识(函数,字符串处理等),把这个计算器改进一下,让他成为一个可以计算更多数据更多算符的高级计算器。
我们是怎么计算一个复杂计算式的?我们总是根据要求列出一个计算式,这个计算式中有数字(整数)和对数字进行操作的算符(+,-,*,/四种运算),然后,从左到右依次计算,最后得到结果。比如,我们要计算
1 + 2 – 3 * 4
我们总是先计算3*4得到12,然后计算1+2得到3,最后计算3-12得到结果-9;而如果我们想用程序对这个字符串表示的计算式进行计算,又该如何进行呢?如果这个计算式比较简单,比如,只有1+2,我们倒是可以找出其中的符号和数字字符,然后将数字字符转换为数字进行计算,而如果这个计算式比较复杂,比如这里的1+2-3*4又该如何进行呢?
回想一下,在数学课上老师是怎么教我们的?面对复杂的计算式,我们可以把它拆分成多个不太复杂的计算式,而不太复杂的计算式我们又可以将它拆分成简单的计算式。这种“大事化小,小事化了”的解题思路,刚好切合了我们的递归函数的设计思路。换句话说,他们都是将一个大问题转化为同类型的小问题,逐渐分解,直到最后可以很容易的得到结果。按照这样的递归函数的设计思路,同时结合数学中计算式的结合律(为了符合结合律,我们查找字符串中的运算符时,从字符串的末尾find_last()开始查找,这样可以避免运算顺序改变后更改运算符号。比如,6-3-3,如果我们从字符串的开始查找运算符,首先找到第一个减号,计算式被分为了(6) – (3-3)两部分,这样计算的结果就不正确了,如果我们从末尾开始查找运算符,则分解后得到(6-3) – (3)这样计算结果就是正确的。)另外还需要注意的是,乘除运算的优先级是高于加减运算的,因为函数的递归,实际上是最先计算最里层的函数,所以,我们应该先分解加减运算,将乘除运算放到最里层。
按照上面的思路分析,我们可以把这个更高级的,可以计算计算式字符串的计算器实现如下:
/* * eval.c * * Created on: 2013年11月1日15:21:51 * Author: Bruce */ #include <string.h> #include <stdio.h> int find_last(const char* s,char a) { int pos = strlen(s); //从字符串末尾位置开始查找 const char* p = s + pos; //如果没有到达字符串开始的前一个位置(s-1) while((s-1) != p) { //如果蛋清位置的字符就是要查找的字符 if(*p == a) { break; //结束查找 } p--; //变换到下一个位置 pos--; } if((s-1) != p) //找到字符 { return pos; } else //未找到 { return -1; } } //取得字符串的左半部分 char* left_str(char* s, int pos) { s[pos] = '