C語言實現簡單計算器程式
阿新 • • 發佈:2020-03-02
這兩天在看一個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 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函式中去新增,或增加一些運算子操作,可以在主迴圈體中增加。
總之,這次學習還是頗有收穫。
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支援我們。