1. 程式人生 > 程式設計 >C語言實現簡單計算器程式

C語言實現簡單計算器程式

這兩天在看一個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函式中去新增,或增加一些運算子操作,可以在主迴圈體中增加。

總之,這次學習還是頗有收穫。

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支援我們。