C語言實現(xiàn)簡單計算器程序
這兩天在看一個C語言寫的計算器程序,做了不少的功夫,跟著作者一步步的進行完善,了解了許多細節(jié)性的東西,在此自己做個總結,加深自己對程序的印象,也算是梳理。
在該計算器程序,能進行加減乘除、sin、cos、exp等操作,同時能進行數(shù)值保存功能。而該計算器使用逆波蘭表示法。即所有運算符都跟在操作數(shù)的后面,比如下列表達式:
(1 - 2) * (4 + 5)采用逆波蘭表示法表示為:1 2 - 4 5 + *
逆波蘭表達法中不需要圓括號,只要知道每個運算符需要幾個操作數(shù)就不會引起歧義。
計算器程序實現(xiàn)很簡單,具體原理如下:
while(/* 下一個運算符或操作數(shù)不是文件結束指示符 */) if(/* 是數(shù) */) /* 將該數(shù)壓入到棧中 */ else if (/* 是運算符 */) /* 彈出所需數(shù)目的操作數(shù) */ /* 執(zhí)行運算 */ /* 將結果壓入到棧中 */ else if (/* 是換行符 */) /* 彈出并打印棧頂?shù)闹?*/ else /* 出錯 */
在程序設計中,使用模塊化思想,getop函數(shù)來進行讀入,該函數(shù)返回一個標識,用來標識讀入的是什么類型。主循環(huán)體中根據該標識執(zhí)行相應的動作。
以下是該程序: (我將所有函數(shù)和變量放在同一文件)
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#define MAXOP 100
#define NUMBER '0' //標識讀入的是數(shù)字
#define NAME 'n' //標識讀入的是字符串(函數(shù)名或非法字符串)
#define ALPHA 26
int getop(char []);
void push (double); //壓棧
double pop(void); //出棧
void clear(void); //清空棧
void mathfnc(char []); //執(zhí)行相應的數(shù)學函數(shù)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++) //初始化用于保存數(shù)值的變量數(shù)組
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 '=': //保存數(shù)值
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 == '-') //用于判斷是負數(shù)還是減操作
{
if (isdigit(c = getch()) || c == '.')
s[++i] = c;
else
{
if (c != EOF)
ungetch(c);
return '-';
}
}
if (isdigit(c)) //收集整數(shù)部分
while (isdigit(s[++i] = c = getch()))
;
if (c == '.') //收集小數(shù)部分
while (isdigit(s[++i] = c = getch()))
;
s[i] = '\0';
if (c != EOF)
ungetch(c);
return NUMBER;
}
/* ----------------------------------------------------------- */
/*
* 引用以下兩個函數(shù)是因為:程序不能確定它已經讀入的輸入是否足夠 *
* 除非超前多讀入一些輸入,在本程序中,讀入一些字符合成一個數(shù)字 *
* 所以在看到第一個非數(shù)字字符之前,已經讀入的數(shù)的完整性是不能確定的
* 由于程序要超前讀入一個字符,這樣就導致最后又一個字符不屬于當前所要讀入的數(shù)
*/
#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;
}
該程序雖然簡單,但是還是存在一些小小的問題,比如沒有數(shù)據時進行pop的話,會打印棧中無數(shù)據同時返回數(shù)值0.0,在循環(huán)體中許多執(zhí)行操作會將該數(shù)值保存到棧中,之后打印該值,用戶體驗度比較差。程序設計方面,模塊化設計使得該程序容易增加功能而不影響其他模塊,比如增加一些數(shù)學函數(shù)處理,在mathfnc函數(shù)中去添加,或增加一些運算符操作,可以在主循環(huán)體中增加。
總之,這次學習還是頗有收獲。
關于計算器的精彩文章請查看《計算器專題》 ,更多精彩等你來發(fā)現(xiàn)!
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。
相關文章
VisualStudio2019解決scanf函數(shù)報錯問題
在 Visual Studio 2019 編輯代碼時,前期剛剛接觸到VS編譯器時存在的困惑,當用scanf()函數(shù),進行輸入時,在運行的時候編譯器會出現(xiàn)警告報錯,本文就來介紹一下解決方法2023-08-08
C/C++?Qt數(shù)據庫與SqlTableModel組件應用教程
SqlTableModel?組件可以將數(shù)據庫中的特定字段動態(tài)顯示在TableView表格組件中,這篇文章將主要介紹SqlTableModel組件一些常用的操作,需要的朋友可以參考一下2021-12-12

