C语言实现简易计算器(加减乘除)

计算器作为课设项目,已完成答辩,先将代码和思路(注释中)上传一篇博客

已增添、修改、整理至无错且可正常运行

虽使用了栈,但初学者可在初步了解栈和结构语法后理解代码

  1. #include <stdlib.h>
  2. #include <stdio.h>
  3. #include <string.h>
  4. #define IsDouble 0
  5. #define IsChar 1
  6. //_______________________________________________________________________________________________________________________________________________________
  7. //1.支持浮点数和字符的栈
  8. typedef struct {
  9. char * buffer;
  10. int typesize;
  11. int top;
  12. int max;
  13. } stack;
  14. stack * CreateStack(int max, int typesize);//创建一个栈
  15. void DestroyStack(stack *);//释放栈的空间
  16. void ClearStack(stack *);//清空栈
  17. int Push(stack *, void *);//入栈
  18. int Pop(stack *, void *);//出栈
  19. int GetSize(stack *s);//得到栈的大小
  20. int GetTop(stack *, void *);//找到栈顶
  21. int IsEmpty(stack *);//判断是否为空栈,空则下溢
  22. int IsFull(stack *);//判断栈是否已满 ,满则溢出
  23. stack * CreateStack(int max, int typesize){
  24. stack * s = (stack*)malloc(sizeof(stack));//为栈s malloc内存
  25. if (!s) return 0;
  26. //为结构中buffer元素malloc内存
  27. s->buffer = (char *)malloc(sizeof(char) * max * typesize);
  28. if (!s->buffer) return 0;
  29. //初始化结构中的栈顶,最大值,类型大小
  30. s->top = -1;
  31. s->max = max;
  32. s->typesize = typesize;
  33. return s;
  34. }
  35. void DestroyStack(stack* s){
  36. free(s->buffer);//先释放buffer的空间
  37. free(s);//在释放s的空间
  38. }
  39. void ClearStack(stack * s){
  40. s->top = -1;//清空栈(栈头位置归零)
  41. }
  42. int Push(stack * s, void * data){
  43. if (IsFull(s)) return 0;//如果栈已满则return 0,防止溢出
  44. //栈未满则将栈头移动打动下一位置,并将data中的元素拷入栈中buffer的第top位置
  45. s->top++;
  46. memcpy(s->buffer + s->top*s->typesize, data, s->typesize);
  47. //入栈成功return 1
  48. return 1;
  49. }
  50. int Pop(stack * s, void * data){
  51. if (IsEmpty(s)) return 0;//出栈判断栈是否为空,若为空则return 0
  52. //栈未空则将buffer中top位置的字符拷入data记录,并让栈头向前移动一个位置
  53. memcpy(data, s->buffer + s->top*s->typesize, s->typesize);
  54. s->top--;
  55. //成功则return 1
  56. return 1;
  57. }
  58. int GetSize(stack *s){
  59. return s -> top+1;//栈头位置+1得到大小
  60. }
  61. int GetTop(stack *s, void * data){
  62. if (IsEmpty(s)) return 0;//如果栈空return 0
  63. //栈不为空则将top位置的字符拷回data记录,得到栈头
  64. memcpy(data, s->buffer + s->top*s->typesize, s->typesize);
  65. //成功则return 1;
  66. return 1;
  67. }
  68. int IsEmpty(stack * s){
  69. return s->top == -1;//如果top为-1则栈空
  70. }
  71. int IsFull(stack * s){
  72. return s->top == s->max-1;//如果top为max-1则栈满
  73. }
  74. //___________________________________________________________________________________________________________________________________________________
  75. //2.定义一个cal类型,其中data存数时sign为IsDouble,存字符时,sign为Ischar
  76. typedef struct {
  77. double data;
  78. char sign;
  79. } cal;
  80. //3.查找对应符号(找到则返回该符号下标)(找不到则说明该部分为数字返回-1)
  81. int SearchCode(char ch){
  82. char * code = "+-*/()@";//@为终止符,算式输入结束
  83. int index = 0;//
  84. while (code[index]){
  85. if (code[index] == ch) return index;
  86. index++;
  87. }
  88. return -1;
  89. }
  90. //4.得到两个符号间的优先级
  91. //与SearchCode相对应,
  92. char GetPriority(char ch, char next){
  93. //创建一个perferen表,第i行(列)对应SearchCode函数中code中的第i个字符
  94. char perferen[7][7] = {
  95. ">><<<>>",
  96. ">><<<>>",
  97. ">>>><>>",
  98. ">>>><>>",
  99. "<<<<<=E",
  100. ">>>>E>>",
  101. "<<<<<E="
  102. };
  103. //找到两个形参对应的字符
  104. int c = SearchCode(ch);
  105. int n = SearchCode(next);
  106. //如果找不到对应运算符(字符不是运算符而是为数字)return E
  107. if (c==-1 || n==-1) return 'E';
  108. //如果找到两个对应运算符则按照优先级表返回两个运算符的优先级
  109. return perferen[c][n];
  110. }
  111. //5.四则运算
  112. double add(double a, double b) { return a+b; }
  113. double sub(double a, double b) { return a-b; }
  114. double mul(double a, double b) { return a*b; }
  115. double ddiv(double a, double b) { return a/b; }
  116. //整合四种运算
  117. double calcu(double a, char ch, double b){
  118. double (*calculation[4])(double,double) = {add,sub,mul,ddiv};
  119. return calculation[SearchCode(ch)](a,b);
  120. }
  121. //6.检测字符串
  122. int CheckStr(char * buffer){
  123. int n;
  124. //遍历字符串确保算式中无非法字符若检测到非法字符return 0,若都合法则return 1
  125. for (n = 0;buffer[n];n++){
  126. if ((SearchCode(buffer[n]) != -1 || buffer[n] == '.' || (buffer[n] >= '0' && buffer[n] <= '9')) && buffer[n] != '@') continue;
  127. else return 0;
  128. }
  129. buffer[n] = '@';//加上终止符,表示算式结束
  130. buffer[n+1] = '\0';
  131. return 1;
  132. }
  133. //7.得到数据转化为double类型存入rs
  134. int GetDigit(char * buffer, int * n, double * rs){
  135. char str[30];
  136. int i,j = 0;
  137. for (i = 0;SearchCode(buffer[*n]) == -1;i++){
  138. str[i] = buffer[*n];//从*n位置开始,将这一串数字字符存入str
  139. (*n)++;
  140. }
  141. str[i] = '\0';
  142. for (i = 0;str[i];i++){
  143. if (str[i] == '.') j++;
  144. }
  145. //如果一段小数有多个小数点或小数点在数字首尾,return 0
  146. if (j>1 || str[i-1] == '.' || str[0] == '.') return 0;
  147. //rs接收转化为double的数据
  148. *rs = atof(str);
  149. //操作成功return 1
  150. return 1;
  151. }
  152. //8.将用户输入的buffer字符串转化为可供程序运算的calstr数组
  153. int resolu(char * buffer, cal * calstr){
  154. int i = 0, j = 0;
  155. cal c;
  156. while (buffer[i]){
  157. if (SearchCode(buffer[i]) == -1){
  158. //如果得到数据不成功则return 0
  159. if (GetDigit(buffer,&i, &c.data) == 0) return 0;
  160. //如果成功得到数据则在c.sign标记为浮点数
  161. c.sign = IsDouble;
  162. //将c存入数组calstr中
  163. calstr[j++] = c;
  164. }
  165. else{
  166. //若符号为运算符
  167. //判断正负号
  168. if (buffer[i] == '-' && (buffer[i-1] == '('||buffer[i-1] == '+'||buffer[i-1] == '-'||buffer[i-1] == '*'||buffer[i-1] == '/') || (i==0 && buffer[0] == '-')){
  169. i++;
  170. if (GetDigit(buffer,&i, &c.data) == 0) return 0;//在符号的下一位开始查找,若找不到数字return 0
  171. //否则,给数字取相反数,c.sign标记为浮点数,存入calstr中
  172. c.data = 0 - c.data;
  173. c.sign = IsDouble;
  174. calstr[j++] = c;
  175. } else
  176. //如果是正号,与符号处理方式同理
  177. if (buffer[i] == '+' && (buffer[i-1] == '('||buffer[i-1] == '+'||buffer[i-1] == '-'||buffer[i-1] == '*'||buffer[i-1] == '/') || (i==0 && buffer[0] == '+')){
  178. i++;
  179. if (GetDigit(buffer, &i, &c.data) == 0) return 0;
  180. c.sign = IsDouble;
  181. calstr[j++] = c;
  182. }
  183. else{
  184. //如果不是正负号,则为运算符,先强制转换为double类型存在c.data里,然后c.sign标记为char类型,存入calstr
  185. c.data = (double)buffer[i++];
  186. c.sign = IsChar;
  187. calstr[j++] = c;
  188. }
  189. }
  190. }
  191. //操作蔡成功则return 1
  192. return 1;
  193. }
  194. //9.计算出结果
  195. int result(cal * calstr, double * rs){
  196. stack * pst = CreateStack(100,sizeof(char));//运算符栈
  197. stack * pnd = CreateStack(100,sizeof(double));//数据栈
  198. double num1,num2;
  199. int n = 0;
  200. char ch = '@';
  201. Push(pst, &ch);
  202. //在转换得到的calstr中遍历直到终止符'@"
  203. while(ch != '@' || !(calstr[n].sign == IsChar && (char)calstr[n].data == '@')){
  204. //如果calstr的n位上是浮点数,则将这个data压栈进入数据栈pnd中
  205. if (calstr[n].sign == IsDouble){
  206. Push(pnd, &(calstr[n].data));
  207. n++;
  208. }
  209. //反之,如果是运算符,则要检测优先级
  210. else{
  211. switch( GetPriority(ch, (char)calstr[n].data)){
  212. //如果运算符优先级较小,则让ch等于优先级大的符号并压入符号栈pst中
  213. case '<':
  214. ch = (char)calstr[n].data;
  215. Push(pst, &ch);
  216. n++;
  217. break;
  218. //如果结果为等号,让符号出栈暂存到ch中
  219. case '=':
  220. if (!Pop(pst, &ch)) return 0;
  221. n++;
  222. break;
  223. //如果ch优先级较高,则将前两个数字及运算符出栈,分别储存至num2,ch,num1中,进行运算,得到的结果再次压栈进入pnd中
  224. case '>':
  225. if (!(Pop(pnd,&num2) && Pop(pst,&ch) && Pop(pnd,&num1))) return 0;
  226. num1 = calcu(num1,ch,num2);
  227. Push(pnd, &num1);
  228. break;
  229. //如果符号顺序出错,return 0
  230. case 'E':
  231. return 0;
  232. }
  233. }
  234. //检测是否可以得到栈顶符号,栈空则return 0
  235. if (!GetTop(pst, &ch)) return 0;
  236. }
  237. //如果栈中得到了最终结果,并且取出pnd中的最终结果到rs,return 1
  238. if (GetSize(pnd) == 1 && GetTop(pnd,rs)){
  239. DestroyStack(pst);
  240. DestroyStack(pnd);
  241. return 1;
  242. }
  243. //否则 return 0
  244. else{
  245. return 0;
  246. }
  247. }
  248. //10.用户交互函数
  249. void treatment()
  250. {
  251. char buffer[100];//用户输入的字符串(算式)
  252. cal calstr[50];//计算用的数组
  253. double rs = 0;//计算结果
  254. printf("Enter your equation:");
  255. gets(buffer);//让用户输入算式buffer
  256. //用户不输入"exit"就不退出
  257. while (!(buffer[0]=='e' && buffer[1]=='x' && buffer[2]=='i' && buffer[3]=='t')){
  258. //检查buffer中字符君合法,成功将buffer转化为用于计算的calstr数组,成功计算出结果存入rs
  259. if (CheckStr(buffer) && resolu(buffer,calstr) && result(calstr,&rs)){
  260. printf("\n%lf\n",rs);
  261. }else{
  262. printf("\nError!\n");
  263. }
  264. printf("Enter \"exit\"to quit");
  265. printf("\nEnter your equation:");
  266. gets(buffer);//再次让用户输入算式
  267. }
  268. printf("\nbye\n");
  269. }
  270. //11.主函数
  271. int main(){
  272. treatment();
  273. }

参考文献链接如下

[参考文献](c语言强化训练——简易计算器 - 石莹 - 博客园 (cnblogs.com))

C语言实现简易计算器(可作加减乘除)的更多相关文章

  1. 大一C语言学习笔记(10)---编程篇--制作简易计算器,支持加,减,乘,除,取余运算,要求 0 bug

    博主自开学初就一直在努力为自己的未来寻找学习方向,学习编程嘛,尽量还是要抱大腿的,所以我就加入了我们学校的智能设备研究所,别的不说,那的学长们看起来是真的很靠谱,学长们的学习氛围也超级浓厚,所以我就打 ...

  2. 剖析简易计算器带你入门微信小程序开发

    写在前面,但是重点在后面 这是教程,也不是教程. 可以先看Demo的操作动图,看看是个什么玩意儿,GitHub地址(https://github.com/dunizb/wxapp-sCalc) 自从微 ...

  3. Python之实现一个简易计算器

    自己动手写计算器 一.功能分析 用户输入一个类似这样 3*( 4+ 50 )-(( 100 + 40 )*5/2- 3*2* 2/4+9)*((( 3 + 4)-4)-4) 这样的表达式,假设表达式里 ...

  4. JavaScript简易计算器

    JavaScript一种直译式脚本语言,是一种动态类型.弱类型.基于原型的语言,内置支持类型.它的解释器被称为JavaScript引擎,为浏览器的一部分,广泛用于客户端的脚本语言,最早是在HTML(标 ...

  5. 微信小程序-简易计算器

    代码地址如下:http://www.demodashi.com/demo/14210.html 一.前期准备工作 软件环境:微信开发者工具 官方下载地址:https://mp.weixin.qq.co ...

  6. 制作一个简易计算器——基于Android Studio实现

    一个计算器Android程序的源码部分分为主干和细节两部分. 一.主干 1. 主干的构成 计算器的布局 事件(即计算器上的按钮.文本框)监听 实现计算 2. 详细解释 假设我们的项目名为Calcula ...

  7. Qt、C++ 简易计算器

    Qt.C++实现简易计算器: 以下内容是我实现这个简易计算器整个过程,其中包括我对如何实现这个功能的思考.中途遇到的问题.走过的弯路 整个实现从易到难,计算器功能从简单到复杂,最开始设计的整个实现步骤 ...

  8. C语言实现简单计算器小项目

    昨天刚安装上devc++,半夜想着练练C语言吧 于是就看到实验楼有一个计算器的项目 之前做过一次,这次写的主要是思路 首先我们先从原理思考jia,实现简单的计算器就要具备加减乘除这些,看普通的计算器也 ...

  9. C语言之简易了解程序环境

    C语言之简易了解程序环境 大纲: 程序的翻译环境 预编译 编译 汇编 链接 程序的运行环境 在ANSI C的任何一种实现中,存在两个不同的环境. 第1种是翻译环境,在这个环境中源代码被转换为可执行的机 ...

随机推荐

  1. js之window对象(慕课网学习笔记)

    javaScript定义了一个变量一个函数都会变成window中的一个成员 var a=1; alert(window.a) //会输出a的值 window基础 创建窗口.调整窗口.移动窗口.关闭窗口 ...

  2. 刷题-力扣-1137. 第 N 个泰波那契数

    1137. 第 N 个泰波那契数 题目链接 来源:力扣(LeetCode) 链接:https://leetcode-cn.com/problems/n-th-tribonacci-number 著作权 ...

  3. 用vue的抽象组件来做一个防止img标签url为空或url地址出错的验证

    看了网上文章学习了下vue的抽象组件,感觉就跟react的高阶组件一样的使用场景,只是更加面向vue的底层编程 ,网上介绍的抽象组件一般有2种用法,1 用来加防抖和节流 2 用来控制按钮是否允许点击做 ...

  4. Shell脚本逐行读取文本内容并拆分,根据条件筛选文件

    时间:2018-11-13 整理:byzqy 需求: 最近帮朋友写了一段脚本,他的需求是根据一份产品清单,去服务器上捞取对应产品编号的测试Log,数量大概有9000~10000条左右.文本内容大致如下 ...

  5. 在已有Win7/10系统电脑中加装Ubuntu18.04(安装双系统)

    准备一台有Win7/10的电脑. 为Ubuntu预留硬盘空间.最好在硬盘最后保留一个空闲分区.(Ubuntu会自动安装到后面的空闲分区) 用Universal USB Installer制作安装盘(U ...

  6. Mybatis源码解析3——核心类SqlSessionFactory,看完我悟了

    这是昨晚的武汉,晚上九点钟拍的,疫情又一次来袭,曾经熙熙攘攘的夜市也变得冷冷清清,但比前几周要好很多了.希望大家都能保护好自己,保护好身边的人,生活不可能像你想象的那么好,但也不会像你想象的那么糟. ...

  7. CSS3 animaion 和 transition 比较

    animation是CSS3的动画属性,可以设置以下六种属性. transition是CSS3的过度属性,可以设置以下四种属性. 从属性上分析,animation可以设定循环次数. 其次,两者的触发条 ...

  8. VUE004. provide与inject的使用(祖先组件隔多层传静态值给子孙组件)

    provide和inject可以通过祖先组件隔三层四层甚至隔着九层妖塔传值给子孙组件. 需要注意的是这样的传值方式是非响应式的,需要结合自身的应用场景,比如将上传的限制条件通过父组件传值给子组件的子组 ...

  9. oh,我的老伙计,你看看这近五十个dapr视频

    oh,我的老伙计,你看看这近五十个 dapr 视频.这不就是你想要的视频资料吗?快来捡走吧! 开始了,但是没完全开始 Dapr 是一个可移植的.事件驱动的运行时,它使任何开发人员能够轻松构建出弹性的. ...

  10. 使用vsCode开发vue项目格式化通用配置

    {   "editor.tabSize": 2,   "editor.fontSize": 18,   "editor.wordWrap": ...