流程图(函数有点多)

核心算法:

先上代码为敬,之后详细拆分每个函数

#include <stdio.h>
#include <stdlib.h>void calculator_run();   //管理运行,输入,计算相关
int input_and_legal(char []);  //输入并验证字符合法
int legal(char);    //验证字符合法
void compute_and_output(char []);   //相关处理,输出结果
double str_to_double(char [],int *);    //字符串转浮点型
int priority(char,char);       //优先级验证
int sign_to_num(char);       //符号转数字,协助优先级验证
int compute_and_legal(double *,double *,char *,char *);   //计算并验证等式有效
int bracket_and_legal(char [],int *);    //验证括号int main()
{char judge;printf("输入s开始,输入e退出\n");scanf(" %c",&judge);if(judge=='s') calculator_run();else if(judge!='e') printf("输入错误\n");return 0;
}void calculator_run()
{char text[1000],judge=1;      //text储存输入文本do{if(input_and_legal(text))      //如果输入合法{compute_and_output(text);      //计算并输出}printf("输入c清除,输入e退出\n");scanf(" %c",&judge);}while(judge!='e');
}int input_and_legal(char text[1000])
{printf("请输入不超过999个字符\n");int i=0,ret=1;do{scanf(" %c",&text[i]);if(!legal(text[i]))       //如果字符非法{printf("输入%c有误!\n",text[i]);ret=0;}}while(i<999 && text[i++]!='=');text[i]='\0';return ret;
}int legal(char ch)
{if(ch>'9' || ch<'0'){if(ch=='+' || ch=='-' || ch=='*' || ch=='/' || ch=='(' || ch==')' || ch=='.' || ch=='='){return 1;}else return 0;}return 1;
}void compute_and_output(char text[1000])
{double number[500];     //储存数字char sign[500];      //储存符号int n_i=0,s_i=0,t_i=0;     //n_i为number索引,s_i为sign索引,t_i为text索引while(text[t_i]!='\0'){if((text[t_i]<='9') && (text[t_i]>='0'))number[n_i++]=str_to_double(text,&t_i);    //储存相关数字if((text[t_i]=='-'|| text[t_i]=='+')&&(t_i==0|| text[t_i-1]=='('))       //用于解决-a,(-a),(+a)问题number[n_i++]=0;sign[s_i++]=text[t_i++];      //储存相关符号while(n_i>1 && s_i>1){int mark=priority(sign[s_i-2],sign[s_i-1]);   //判断优先级if(mark==1)      //如果优先级高{--s_i,--n_i;if(!compute_and_legal(&number[n_i-1],&number[n_i],&sign[s_i-1],&sign[s_i]) return ;    //如果计算有误if(!bracket_and_legal(sign,&s_i)) return ;      //如果缺少右括号}else if(mark==0) break;     //如果优先级低else if(mark==-1) return;      //异常情况,如1...,1+++等}}if(n_i==1 && s_i==1) printf("结果:%lf\n",number[0]);  //如果表达式有效else printf("表达式有误!\n");
}double str_to_double(char text[1000],int *t_i)
{char num_substr[100];int n_i=0;do{num_substr[n_i]=text[*t_i];++(*t_i);++n_i;}while((text[*t_i]>='0' && text[*t_i]<='9')||(text[*t_i]=='.' && text[*t_i+1]>='0' && text[*t_i+1]<='9'));      //数字为正常情况,例如1...则异常num_substr[n_i]='\0';return atof(num_substr);      //调用库函数转换
}int priority(char sign1,char sign2)
{int num1=sign_to_num(sign1);      //将符号转为整形,便于判断优先级int num2=sign_to_num(sign2);if(num1*num2==0)     //如果符号中有非法运算符,如sign1='.',或sign2='.'{printf("表达式有误!\n");return -1;      //符号异常}if(num1<num2 || num1==5) return 0;   //如果优先级小或sign1='(',如(1+2.....,应该先执行+而不是(else return 1;     //优先级高
}int sign_to_num(char sign)
{swotch(sign){case '(': return 5;case '*': return 4;case '/': return 4;case '+': return 3;case '-': return 3;case ')': return 2;case '=': return 1;}return 0;    //符号异常
}int compute_and_legal(double *number1,double *number2,char *sign1,char *sign2)
{switch(*sign1){case '+': *number1+=*number2;break;case '-': *number1-=*number2;break;case '*': *number1*=*number2;break;case '/':if(*number2!=0) *number1/=*number2;else{printf("除数为0错误!\n");return 0;}}*sign1=*sign2;return 1;
}int braket_and_legal(char sign[],int *s_i)
{if(sign[*s_i-1]==')'){if(sign[*s_i-2]=='('){(*s_i)-=2;}else {printf("右括号错误!\n");return 0;}}return 1;
}

首先是整个程序的总枢纽,不管是输入,还是计算,最终都要回到这个函数,calculator_run:

void calculator_run()
{char text[1000],judge=1;      //text储存输入文本do{if(input_and_legal(text))      //如果输入合法{compute_and_output(text);      //计算并输出}printf("输入c清除,输入e退出\n");scanf(" %c",&judge);}while(judge!='e');
}

在编写这个函数时,在输入方面遇到了很大的问题,scanf函数在输入字符时会接受回车,会导致莫名奇妙的BUG,在设计之初,是类似于这样的结构:

do{while(....){scanf("%s",.....);if(....);}printf("输入c清除,输入e结束\n");scanf("%c",.....);
}while(....);

原意是第一个scanf接受字符然后检查是否非法,结束表达式的输入后第二个scanf输入,判断是否退出,然而在第一个scanf输入完成,回车后第二个scanf接受了回车,接下来本该被第二个scanf接受的c或者是e被第一个scanf意外的接受了,判断字符非法;又或者是第二个scanf输入c回车继续运行时,第一个scanf接受回车判错;
接下来就想有没有一种方法是能够忽略回车的,于是查到了在“%c”前面加一个空格能够忽略回车“ %c”
原文
输入方面遇到的另一个问题是第一个scanf接受想要的数字,第二个scanf接受符号,最后一个scanf接受是否退出的字符,同样是回车的问题,不过另一个让我不得不放弃这个方案的问题是正负号的判断

while(....){while(....){scanf("%d",....);scanf(" %c",....);if(.....);}scanf(" %c",.....);
}

我的本意是第一个数如果为负的话就被第一个scanf接受了,这样道是挺方便,但问题在于有了括号之后正负号是作为数字的符号还是作为一个运算符就不好界定了,如-1+(1+1)-1,此时-1被第一个scanf接受,倒是正常,问题出在‘)’之后的负号,在第二个scanf接受了’)‘之后,负号会被第一个scanf接受,此时式子成为类似(-1)+(1+1) (-1)这样的了,这显然是错误的式子。之后我想用无符号的浮点型,这样第一个scanf应该不会接受负号了,但c语言好像没有无符号浮点型,没有想到解决的办法,不得已放弃这个方案。改用直接输入整个表达式,然后再进行处理,这样应该好处理正负号问题。
因为我本人其实挺讨厌一个函数体特别长,看着难受,所以我把输入和验证,还有计算和输出这些功能外包出去了,减少calculator_run函数的函数体。

然后是输入验证函数了,input_and_legall

int input_and_legall(char text[1000])
{printf("请输入不超过999个字符\n");int i=0,ret=1;do{scanf(" %c",&text[i]);if(!legal(text[i]))       //如果字符非法{printf("输入%c有误!\n",text[i]);ret=0;}}while(i<999 && text[i++]!='=');text[i]='\0';return ret;
}

同样,为了减少函数体,我把验证字符合法的功能外包出去了。最开始是判断字符非法后直接返回值,然后在calculator_run这个函数中打印输入字符有误的

if(!legal(text[i]))       //如果字符非法
{return 0;
}

但这样也产生了一个问题,判断非法后由于没有输入回车,导致函数虽然返回了但字符非法的提示并没有打印出来,还是停留在输入界面的,于是非法字符之后的第一个字符被calculator_run的判断是否退出的scanf接受,如果是e就退出了,如果是其它字符又调用当前函数,又导致意想不到的结果,为了解决这个问题,我干脆不直接返回了,当输入’=‘时再返回。

input_and_legal函数调用legal函数验证字符的有效性

int legal(char ch)
{if(ch>'9' || ch<'0'){if(ch=='+' || ch=='-' || ch=='*' || ch=='/' || ch=='(' || ch==')' || ch=='.' || ch=='='){return 1;}else return 0;}return 1;
}

唯一的一点就是使用’||‘进行判断是否符合合法符号而不是使用’&&‘进行判断是否不是合法符号的原因是第一个方案只要有一个满足就返回了,而第二种方案要判断是否不满足所有的合法符号,徒增判断。

输入的内容完成,接下来是对输入的字符进行处理的环节了,这个环节有点麻烦,compute_and_output

void compute_and_output(char text[1000])
{double number[500];     //储存数字char sign[500];      //储存符号int n_i=0,s_i=0,t_i=0;     //n_i为number索引,s_i为sign索引,t_i为text索引while(text[t_i]!='\0'){if((text[t_i]<='9') && (text[t_i]>='0'))number[n_i++]=str_to_double(text,&t_i);    //储存相关数字if((text[t_i]=='-'|| text[t_i]=='+')&&(t_i==0|| text[t_i-1]=='('))       //用于解决-a,(-a),(+a)时的符号问题number[n_i++]=0;sign[s_i++]=text[t_i++];      //储存相关符号while(n_i>1 && s_i>1){int mark=priority(sign[s_i-2],sign[s_i-1]);   //判断优先级if(mark==1)      //如果优先级高{--s_i,--n_i;if(!compute_and_legal(&number[n_i-1],&number[n_i],&sign[s_i-1],&sign[s_i]) return ;    //如果计算有误if(!bracket_and_legal(sign,&s_i)) return ;      //如果缺少右括号}else if(mark==0) break;     //如果优先级相等或者低else if(mark==-1) return;      //异常情况,如1...,1+++等}}if(n_i==1 && s_i==1) printf("结果:%lf\n",number[0]);  //如果表达式有效else printf("表达式有误!\n");
}

首先判断文本是数字,然后调用一个str_to_double函数,将相关字符转成浮点型数据,同时传过去字符串索引的指针,意味着这个函数会改变字符串的索引,当函数返回后,索引应该指向一个符号而不是数字。
然后加了这样一个语句

if((text[t_i]=='-'|| text[t_i]=='+')&&(t_i==0|| text[t_i-1]=='('))       //用于解决-a,(-a),(+a)时的符号问题number[n_i++]=0;

主要是为了解决-a,(+a…,(-a…,的问题,解决方案是加零,然后形成了这样子的表达式0-a,(0+a…,(0-a…,这样就将所有的符号都参与运算了,所有的数字都为正,不存在符号问题。
然后进入一个while循环。
首先调用priority函数判断符号优先级,mark接受返回值。

1为前一符号优先级大于后等于一符号,如1+2+3,1*3+2,…(…1+2),此时调用计算并且判断数据有效性的函数compute_and_legal,如1+2-…,调用后应该为3-…,然后再调用判断括号有效性的函数bracket_and_legal,注意因为参数为指针,所以这个函数执行的也不仅仅是判断,它会在括号闭合时将符号指针s_i前移,因为括号里的表达式已经执行了,应该把括号去掉,如…+(2+3)…,在compute_and_legal执行后应该是类似…+(5)…,然后在执行了这个函数之后应该是类似…+5…这样子的形式。

返回值为0代表前一个符号优先级低于后一个符号,或者前一个符号为’(’,如1+2*…,…(1*…,此时都不应该执行操作,于是直接退出当前循环。

返回值为-1代表着符号有误,如1+.,1…,(.等等,此时表达式是有误的,不应该在执行下去了,函数返回。

退出循环之后判断结果是否是最终结果,如1+2++,++++,等等,因为最终符号有剩余,所以不是最终结果。

在compute_and_legal中调用了str_to_double函数

double str_to_double(char text[1000],int *t_i)
{char num_substr[100];int n_i=0;do{num_substr[n_i]=text[*t_i];++(*t_i);++n_i;}while((text[*t_i]>='0' && text[*t_i]<='9')||(text[*t_i]=='.' && text[*t_i+1]>='0' && text[*t_i+1]<='9'));      //数字为正常情况,例如1...则异常num_substr[n_i]='\0';return atof(num_substr);      //调用库函数转换
}

此函数主要就是搜寻一个数字字串,并把它转化成为一个浮点数类型,顺便将字符串的索引递增了

在compute_and_legal函数中调用了priority函数

int priority(char sign1,char sign2)
{int num1=sign_to_num(sign1);      //将符号转为整形,便于判断优先级int num2=sign_to_num(sign2);if(num1*num2==0)     //如果符号中有非法运算符,如sign1='.',或sign2='.'{printf("表达式有误!\n");return -1;      //符号异常}if(num1<num2 || num1==5) return 0;   //如果优先级小或sign1='(',如(1+2.....,应该先执行+而不是(else return 1;     //优先级高
}

因为直接判断优先级很麻烦,一个加减,一个乘除就需要写出

if((sign1=='*' || sign1=='/') && (sign2=='+' || sign2=='-')) return 1;
if ((sign1=='+' || sign1=='-') && (sign2=='*' || sign2=='/')) return 0;

这样就很烦,如果能把符号转化成数字,直接判断数字大小比较优先级该有多好,于是有了sign_to_num

int sign_to_num(char sign)
{swotch(sign){case '(': return 5;case '*': return 4;case '/': return 4;case '+': return 3;case '-': return 3;case ')': return 2;case '=': return 1;}return 0;    //符号异常
}

compute_and_output中调用了compute_and_legal来计算值并返回值的有效性相关信息,这里的有效性是除数不为零

int compute_and_legal(double *number1,double *number2,char *sign1,char *sign2)
{switch(*sign1){case '+': *number1+=*number2;break;case '-': *number1-=*number2;break;case '*': *number1*=*number2;break;case '/':if(*number2!=0) *number1/=*number2;else{printf("除数为0错误!\n");return 0;}}*sign1=*sign2;return 1;
}

conpute_and_output中调用了bracket函数来进行括号的闭合判断和对括号闭合的处理

int braket_and_legal(char sign[],int *s_i)
{if(sign[*s_i-1]==')'){if(sign[*s_i-2]=='('){(*s_i)-=2;}else {printf("右括号错误!\n");return 0;}}return 1;
}

来一堆脑残的数据验证一下:



但是还是有点小BUG,虽然这在数学上来说并没问题,但我想应该很少人会这样写吧,主要是这个问题很难改正,调整代码解决了这个问题后,又出现了一些新的问题,比如输入某种形式的表达式后进入一个死循环再也出不来了。

c语言实现一个计算器相关推荐

  1. 用C语言写一个计算器

    用C语言写一个计算器,除了四则混合运算之外,还支持三角函数和绝对值等函数. PS E:\Code\PL\calc> .\a.exe abs(3*5-4^2) abs(3*5-4^2)=1.000 ...

  2. 用栈实现计算器c语言报告,请问,用c语言做一个计算器 包括+-*/()的运算 用栈 该怎么做...

    该楼层疑似违规已被系统折叠 隐藏此楼查看此楼 #include #include #define UINT unsigned int struct LOGIC { UINT logic,site; } ...

  3. c语言用栈实现计算器加法运算,请问,用c语言做一个计算器 包括+-*/()的运算 用栈 该怎么做...

    该楼层疑似违规已被系统折叠 隐藏此楼查看此楼 #include #include #define UINT unsigned int struct LOGIC { UINT logic,site; } ...

  4. c语言怎样计算栈的长度,请问,用c语言做一个计算器 包括+-*/()的运算 用栈 该怎么做...

    该楼层疑似违规已被系统折叠 隐藏此楼查看此楼 #include #include #define UINT unsigned int struct LOGIC { UINT logic,site; } ...

  5. 栈的应用c语言计算器思路,请问,用c语言做一个计算器 包括+-*/()的运算 用栈 该怎么做...

    该楼层疑似违规已被系统折叠 隐藏此楼查看此楼 #include #include #define UINT unsigned int struct LOGIC { UINT logic,site; } ...

  6. 写一个计算器(C语言版本),可以求出:整数的加,减,乘,除四则运算

    写一个计算器(C语言版本),可以求出:整数的加,减,乘,除四则运算! 对于求出整数的加减乘除问题,我想大家现如今看见笔者此篇博文时候!大多都已经能够正确写出准确的代码来求整数的加减乘除了!其实对于计算 ...

  7. 十位数连加 c语言,用C语言编写一个简易计算器可实现加减乘除,连加连减,连乖连除....

    用C语言编写一个简易计算器可实现加减乘除,连加连减,连乖连除. 用C语言编写一个简易计算器可实现加减乘除,连加连减,连乖连除. 人气:435 ℃时间:2020-04-10 06:55:13 优质解答 ...

  8. 用C语言实现一个简单的计算器代码

    #include <stdio.h> #include <math.h> #include <stdlib.h> //预处理指令 int main(void) {d ...

  9. java语言计算器怎么写_求助,一个计算器的括号功能怎么写啊。

    求助,一个计算器的括号功能怎么写啊. import javax.swing.*; import javax.swing.event.*; import java.awt.*; import java. ...

  10. c语言计算器系统流程图,这是一个计算器程序 请各位高手帮我画一个流程图

    这是一个计算器程序 请各位高手帮我画一个流程图 #include #include #include char token; void error(void) { printf("stder ...

最新文章

  1. Python3连接PostgreSQL(10.5)数据库
  2. zookeeper都有哪些使用场景
  3. linux 5 防火墙,CentOS 5 Linux iptables防火墙的配置
  4. 互联网金融产品需要什么样的产品经理?
  5. mysql server 2012_Windows server 2012 搭建mysql双主
  6. spring核心之AOP学习总结二
  7. java nio详解,Java NIO API详解
  8. java web登录action_JavaWeb中登陆功能
  9. ubuntu下gvim启动出现gtk warning Invalid input string
  10. 终于有人把Docker讲清楚了
  11. replaceFirst、replaceAll、replace区别
  12. Mapbar 地图 API 概念   技术文档
  13. VS2008添加ODBC数据源崩溃
  14. 【转载】聪明说话35招
  15. 凸优化第二章凸集 2.2 重要例子(仿射集合和凸集)
  16. HDU2516 取石子游戏(斐波那契)
  17. 精选PHP毕业设计12套——源码+论文完整资源
  18. K8S入门系列(1)-Windows10安装Docker,配置阿里云加速器
  19. 点击谷歌浏览器安装包没有反应
  20. 数据仓库之事实表和维度表

热门文章

  1. 立通信电杆——水泥杆
  2. 华师大的计算机专业属于提前批吗,华东师范大学提前批上海录取线确定
  3. mac虚拟机parallels装Ubuntu无法联网
  4. python逐步回归筛选变量_SPSS进行逐步回归分析
  5. Azure:云平台概述
  6. 出行即服务(MAAS)框架
  7. python组合数_使用python解决排列组合问题
  8. 讨论《蔚蓝(Celeste)》的设计
  9. 【 移动硬盘安装Ubuntu18.04】
  10. 计算机软件类自然科学基金标书,国家自然科学基金标书撰写——体会6(转发仅供参考)...