C语言实现简单计算器程序

时间:2021-05-20

这两天在看一个C语言写的计算器程序,做了不少的功夫,跟着作者一步步的进行完善,了解了许多细节性的东西,在此自己做个总结,加深自己对程序的印象,也算是梳理。

在该计算器程序,能进行加减乘除、sin、cos、exp等操作,同时能进行数值保存功能。而该计算器使用逆波兰表示法。即所有运算符都跟在操作数的后面,比如下列表达式:
(1 - 2) * (4 + 5)采用逆波兰表示法表示为:1 2 - 4 5 + *
逆波兰表达法中不需要圆括号,只要知道每个运算符需要几个操作数就不会引起歧义。

计算器程序实现很简单,具体原理如下:

while() if() else if () else if () else

在程序设计中,使用模块化思想,getop函数来进行读入,该函数返回一个标识,用来标识读入的是什么类型。主循环体中根据该标识执行相应的动作。

以下是该程序: (我将所有函数和变量放在同一文件)

#include <stdlib.h>#include <stdio.h>#include <string.h>#define MAXOP 100#define NUMBER '0' //标识读入的是数字#define NAME 'n' //标识读入的是字符串(函数名或非法字符串)#define ALPHA 26 int getop(char []); void push (double); //压栈double pop(void); //出栈void clear(void); //清空栈void mathfnc(char []); //执行相应的数学函数sin、cos、exp等int main(void){ int type; int i, var = 0; double op1, op2,v; char s[MAXOP]; double variable[ALPHA]; for (i = 0; i < ALPHA; i++) //初始化用于保存数值的变量数组 variable[i] = 0.0; while ((type = getop(s)) != EOF) //读取输入 { switch (type) { case NUMBER: push (atof(s)); break; case NAME: mathfnc(s); break; case '+': push (pop() + pop()); break; case '*': push (pop() * pop()); break; case '-': op2 = pop(); push (pop() - op2); break; case '/': op2 = pop(); if (op2 != 0.0) push (pop() / op2); else printf ("error: zero divisor\n"); break; case '%': op2 = pop(); if (op2 != 0.0) push (fmod(pop(), op2)); else printf ("error: zero divisor\n"); break; case '?': //打印栈顶元素 op2 = pop(); printf ("\t%.8g\n", op2); push (op2); break; case '=': //保存数值 pop(); if (var >= 'A' && var <= 'Z') variable[var - 'A'] = pop(); else printf ("error: no variable name\n"); break; case 'c': clear(); break; case 'd': //复制栈顶元素 op2 = pop(); push(op2); push(op2); break; case 's': //交换栈元素 op1 = pop(); op2 = pop(); push(op1); push(op2); case '\n': v = pop(); //v保存最后的一次结果 printf ("\t%.8g\n", v); break; default: if (type >= 'A' && type <= 'Z') push(variable[type - 'A']); else if (type == '@') //输入的字符@表示最近一次结果值 push(v); else printf ("error: unknown command %s\n", s); break; } var = type; } return 0;}#define MAXVAL 100int sp = 0; //标识栈顶double val[MAXVAL];void push(double f){ if (sp < MAXVAL) val[sp++] = f; else printf ("error: stack full, can't push %g\n", f);}double pop(void){ if (sp > 0) return val[--sp]; else { printf ("error: statck empty\n"); return 0.0; } }void clear(void){ sp = 0;}void mathfnc (char s[]){ double op2; if (strcmp (s, "sin") == 0) push(sin(pop())); else if(strcmp (s, "cos") == 0) push(cos(pop())); else if(strcmp (s, "exp") == 0) push(exp(pop())); else if(strcmp (s, "pow") == 0) { op2 = pop(); push (pow(pop(), op2)); } else printf ("error: %s not supported\n", s);}#include <ctype.h>int getch(void);void ungetch(int);int getop(char s[]){ int i, c; while ((s[0] = c = getch()) == ' ' || c == '\t') //过滤开头的空白字符 ; s[1] = '\0'; i = 0; if (islower(c)) //判断是否为小写字母,也即读取由小写字母组成的字符串 { while (islower(s[++i] = c = getch())) ; s[i] = '\0'; if (c != EOF) ungetch(c); if (strlen (s) > 1) return NAME; else return c; } if (!isdigit(c) && c != '.' && c != '-') return c; if (c == '-') //用于判断是负数还是减操作 { if (isdigit(c = getch()) || c == '.') s[++i] = c; else { if (c != EOF) ungetch(c); return '-'; } } if (isdigit(c)) //收集整数部分 while (isdigit(s[++i] = c = getch())) ; if (c == '.') //收集小数部分 while (isdigit(s[++i] = c = getch())) ; s[i] = '\0'; if (c != EOF) ungetch(c); return NUMBER;}/* * 引用以下两个函数是因为:程序不能确定它已经读入的输入是否足够 * * 除非超前多读入一些输入,在本程序中,读入一些字符合成一个数字 * * 所以在看到第一个非数字字符之前,已经读入的数的完整性是不能确定的 * 由于程序要超前读入一个字符,这样就导致最后又一个字符不属于当前所要读入的数 */#define BUFSIZE 100char buf[BUFSIZE];int bufp = 0;int getch(void) { return (bufp > 0) ? buf[--bufp] : getchar();}void ungetch (int c){ if (bufp >= BUFSIZE) printf ("ungetch: too many characters\n"); else buf[bufp++] = c;}

该程序虽然简单,但是还是存在一些小小的问题,比如没有数据时进行pop的话,会打印栈中无数据同时返回数值0.0,在循环体中许多执行操作会将该数值保存到栈中,之后打印该值,用户体验度比较差。程序设计方面,模块化设计使得该程序容易增加功能而不影响其他模块,比如增加一些数学函数处理,在mathfnc函数中去添加,或增加一些运算符操作,可以在主循环体中增加。

总之,这次学习还是颇有收获。

关于计算器的精彩文章请查看《计算器专题》 ,更多精彩等你来发现!

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。

声明:本页内容来源网络,仅供用户参考;我单位不保证亦不表示资料全面及准确无误,也不保证亦不表示这些资料为最新信息,如因任何原因,本网内容或者用户因倚赖本网内容造成任何损失或损害,我单位将不会负任何法律责任。如涉及版权问题,请提交至online#300.cn邮箱联系删除。

相关文章