服务器之家

服务器之家 > 正文

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

时间:2021-08-20 12:00     来源/作者:talent_CYJ

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

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

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

?
1
2
3
4
5
6
7
8
9
10
11
while/* 下一个运算符或操作数不是文件结束指示符 */
 if/* 是数 */
 /* 将该数压入到栈中 */
 else if /* 是运算符 */
 /* 弹出所需数目的操作数 */
 /* 执行运算 */
 /* 将结果压入到栈中 */
 else if /* 是换行符 */
 /* 弹出并打印栈顶的值 */
 else
 /* 出错 */

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

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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
#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 100
 
int 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 100
char 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函数中去添加,或增加一些运算符操作,可以在主循环体中增加。

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

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

原文链接:https://blog.csdn.net/talent_CYJ/article/details/51792537

标签:

相关文章

热门资讯

yue是什么意思 网络流行语yue了是什么梗
yue是什么意思 网络流行语yue了是什么梗 2020-10-11
2020微信伤感网名听哭了 让对方看到心疼的伤感网名大全
2020微信伤感网名听哭了 让对方看到心疼的伤感网名大全 2019-12-26
背刺什么意思 网络词语背刺是什么梗
背刺什么意思 网络词语背刺是什么梗 2020-05-22
苹果12mini价格表官网报价 iPhone12mini全版本价格汇总
苹果12mini价格表官网报价 iPhone12mini全版本价格汇总 2020-11-13
2021年耽改剧名单 2021要播出的59部耽改剧列表
2021年耽改剧名单 2021要播出的59部耽改剧列表 2021-03-05
返回顶部