⽤c语⾔实现⼀个简单的计算器(数据结构)
简易计算器
整体思想:⾸先我们需要知道,这个算法的核⼼问题在于计算顺序的问题,如何让我们的程序去按照正确的顺序去计算⼀个算术表达式。下⾯举个例⼦ 【2x2+3x(2+4)-6】 这个式⼦对于我们来说,计算起来不难,先计算括号内的2+4=6。然后计算最左边的2x2=4。接着是3x6=18。然后4+18=22。最后22-6=16。就算完了,这是因为我们知道括号内优先,然后是乘除,接着是加减。
同等级符号计算按照从左到右的顺序。所以我们会选择性的去按照既定的顺序来读取,算术,代换,再算。但计算机是不能选择性的读取的。它遵循这从左向右的读取顺序,这时候我们需要⽤到栈来存储暂时不⽤的数据与符号以及它们的顺序。等到轮到这些运算符的时候,再将它们从栈中按顺序提取出来进⾏运算。
运算顺序思想:⾸先咱来看看符号运算顺序,即括号优先,其次乘除,再者加减,同级从左向右。那么既然运算的顺序和符号息息相关,我们便需要从这些符号下⼿来整合出⼀个决定运算顺序的函数了,我们先以计算机⾓度来阅读下上⾯的式⼦,⼀步步捋出应该怎么去计算,再说使⽤代码实现。
从左向右阅读运算:还是上⾯那个例⼦【2x2+3x(2+4)-6】 ,从左向右读取,我们先读到⼀个数字‘2’,
然后是运算符‘x’,接着是数字‘2’,这时已经有俩个数字与⼀个夹在中间的运算符了,那么问题来了我们该不该运算呢?显然是⽆法明确知道的,因为我们需要确定这个乘号直接相接的前后运算符有没有⽐它更优先的存在,如果有那么它所邻接的数字应该优先参与这个优先级更⾼的符号的运算。好,现在我们回到式⼦上,我们现在所读取到的乘号它之前没有运算符,所以我们需要知道它之后这个紧接的运算符与它谁的优先级⾼。那么继续向后读取,读到‘+’,加号优先级⽐乘号⼩。也就是说,乘号所紧接的前后符号,优先级均低于这个乘号,那么它紧接的数字就优先与它进⾏运算了,即我们可以计算2x2了。有⼈会问了,之前我们计算的时候不是最优先计算的括号内的2+4吗,为什么到这⾥先算了2x2,这个顺序其实并不影响我们的计算结果,因为按照之前的运算,我们算完括号内的内容后,以结果去代替原括号,加⼊原计算式内进⾏计算,所以在从左向右阅读运算中,我们只需要在读取到括号时,将其中内容作为⼀个完整的运算式,最优先算出其中的值,然后取代其位置即可。
符号运算顺序规则 ok,以上就是我们如何在从左向右阅读顺序之下去判断⼀个符号该不该运算,那么我们可以初步总结出以下规则来进⾏计算:
1. 读取到括号时将括号内内容优先计算出来,然后取代原括号运算式的位置。
2. 读取到运算符时⽐较其与紧接的前后两个运算符的优先级,若均优先,进⾏计算,否则进⾏搁置。
改版符号运算顺序规则但是⼜有问题了,这样的规则⽆法直观的转化成代码,仅供我们理解,所以我
们对规则在进⾏⼀定的细致整改,并为了让计算机知道我们的运算式何时结束,我们向符号栈栈底和运算式最后⾯加上‘#’作为界标,当俩界标相遇表⽰运算结束,(所以界标‘#’优先级最低)然后编制改良后的规则:
3. 读取到‘#’时若其紧接的前⾯的算术运算符为‘#’则表⽰结束,否则对前接符号进⾏出栈运算。
4. 读取到‘+’,‘-’,若其前接运算符为‘#’则搁置⼊栈,否则将其前接符号弹出运算。
5. 读取到’x’,’/’,若其前接运算符为‘x’或’/'则将其前接符号进⾏出栈运算,否则搁置⼊栈。
6. 读取到‘(’进⾏直接进⾏搁置⼊栈。
7. 读取到‘)’,若其前接符号为‘(’则将两个符号都销毁。否则将其前接符号出栈运算。
然后根据上⾯的规则,我们编写⼀个判断运算顺序的函数:
int Preemption(char a,char b) //符号优先级⽐较,a为当前读⼊,b为栈顶元素
{
int c; //c反馈指令信息 0.结束 1.弹出 2.进栈 3.删除当前元素及栈顶元素 4.
报错
switch(a)
{
case '#':if(b=='#') c=0;
else c=1;break;
case '+':if(b=='#'||b=='(') c=2;
else c=1;break;
case '-':if(b=='#'||b=='(') c=2;
else c=1;break;
case '*':if(b=='*'||b=='/') c=1;
else c=2;break;
case '/':if(b=='*'||b=='/') c=1;
else c=2;break;
case '(':c=2;break;
case ')':if(b=='(') c=3;
else c=1;break;
default :c=4;break;
}
return c;
}
这个函数中我使⽤c作为反馈指令的媒介,函数返回值为c,⽽c的不同值代表不同的指令情况: 0.结束运算 1.弹出符号进⾏运算 2.符号搁置进栈 3.删除当前元素及栈顶元素(俩括号相遇) 4.报错(读到⽆法识别的字符) 。只需要在主函数中使⽤⼀个switch函数接受这个指令并对应执⾏即可。
读取字符串中的多位⼗进制数字由于我们输⼊的算术表达式是⼀个字符串,所以从中直接性识别出来的只能有个位数字,⽽且进⾏储存时需要对其进⾏这样⼀个运算(例如:int a,char c,a=c-‘0’;)。如果我们读取⼀个数字就直接⽤这种⽅式存⼊数字栈中,那么我们存到的⼀定只能是个位数字。所以我们现在需要⼀块代码来负责数字的读取,确保其可以读取到多位数字。我写的代码如下:
if(*p<='9'&&*p>='0')
{
a=a*10+(*p-'0');
if(*(p+1)>'9'||*(p+1)<'0')
{
*p4++=a;
a=0;
}
p++;
}
其中p是指向算术表达式的指针,p4是指向数字栈的指针
ok,⼤致所需要注意的事项就是这些了,下⾯是我的完整代码:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int Preemption(char a,char b) //符号优先级⽐较,a为当前读⼊,b为栈顶元素
{
int c; //c反馈指令信息 0.结束 1.弹出 2.进栈 3.删除当前元素及栈顶元素 4.报错
switch(a)
{
case '#':if(b=='#') c=0;
else c=1;break;
case '+':if(b=='#'||b=='(') c=2;
else c=1;break;
case '-':if(b=='#'||b=='(') c=2;
else c=1;break;
case '*':if(b=='*'||b=='/') c=1;
else c=2;break;
case '/':if(b=='*'||b=='/') c=1;
else c=2;break;
case '(':c=2;break;
case '(':c=2;break;
case ')':if(b=='(') c=3;
else c=1;break;
default :c=4;break;
}
return c;
}
int main()
{
char str[50]={"\0"};
char *p=str;
double *p3,*p4,a=0,b=0;
char *p1,*p2;
char stack1[20]; //符号栈栈顶指针p2,栈底指针p1 double stack2[20]; //数字栈栈顶指针p4,栈底指针p3 p1=p2=stack1;
p3=p4=stack2;
*p2++='#';
printf("请输⼊需要计算的算术表达式:");
gets(str);
strcat(str,"#");
while(*p!='\0')
{
if(*p<='9'&&*p>='0')
{
a=a*10+(*p-'0');
if(*(p+1)>'9'||*(p+1)<'0')
{
*p4++=a;
a=0;
}
p++;
}
一个完整的c语言程序else
{
switch(Preemption(*p,*(p2-1)))
{
case 0:
printf("计算结果为:%lf\n",*p3);
p++;
break;
case 1:
b=*--p4;
switch(*(--p2))
{
case '+':*(p4-1)=*(p4-1) + b;break;
case '*':*(p4-1)=*(p4-1) * b;break;
case '-':*(p4-1)=*(p4-1) - b;break;
case '/':*(p4-1)=*(p4-1) / b;break;
}
break;
case 2:
*p2++=*p++;
break;
case 3:
p++;
p2--;
break;
case 4:
printf("程序读到了⽆法计算的符号,出错了\n");
p++;
break;
}
}
}
return 0;
return 0; }
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论