C語言實現簡易計算器(加減乘除)
計算器作為課設項目,已完成答辯,先將代碼和思路(注釋中)上傳一篇博客
已增添、修改、整理至無錯且可正常運行
雖使用了棧,但初學者可在初步了解棧和結構語法后理解代碼
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#define IsDouble 0
#define IsChar 1
//_______________________________________________________________________________________________________________________________________________________
//1.支持浮點數和字符的棧
typedef struct {
char * buffer;
int typesize;
int top;
int max;
} stack;
stack * CreateStack(int max, int typesize);//創建一個棧
void DestroyStack(stack *);//釋放棧的空間
void ClearStack(stack *);//清空棧
int Push(stack *, void *);//入棧
int Pop(stack *, void *);//出棧
int GetSize(stack *s);//得到棧的大小
int GetTop(stack *, void *);//找到棧頂
int IsEmpty(stack *);//判斷是否為空棧,空則下溢
int IsFull(stack *);//判斷棧是否已滿 ,滿則溢出
stack * CreateStack(int max, int typesize){
stack * s = (stack*)malloc(sizeof(stack));//為棧s malloc內存
if (!s) return 0;
//為結構中buffer元素malloc內存
s->buffer = (char *)malloc(sizeof(char) * max * typesize);
if (!s->buffer) return 0;
//初始化結構中的棧頂,最大值,類型大小
s->top = -1;
s->max = max;
s->typesize = typesize;
return s;
}
void DestroyStack(stack* s){
free(s->buffer);//先釋放buffer的空間
free(s);//在釋放s的空間
}
void ClearStack(stack * s){
s->top = -1;//清空棧(棧頭位置歸零)
}
int Push(stack * s, void * data){
if (IsFull(s)) return 0;//如果棧已滿則return 0,防止溢出
//棧未滿則將棧頭移動打動下一位置,並將data中的元素拷入棧中buffer的第top位置
s->top++;
memcpy(s->buffer + s->top*s->typesize, data, s->typesize);
//入棧成功return 1
return 1;
}
int Pop(stack * s, void * data){
if (IsEmpty(s)) return 0;//出棧判斷棧是否為空,若為空則return 0
//棧未空則將buffer中top位置的字符拷入data記錄,並讓棧頭向前移動一個位置
memcpy(data, s->buffer + s->top*s->typesize, s->typesize);
s->top--;
//成功則return 1
return 1;
}
int GetSize(stack *s){
return s -> top+1;//棧頭位置+1得到大小
}
int GetTop(stack *s, void * data){
if (IsEmpty(s)) return 0;//如果棧空return 0
//棧不為空則將top位置的字符拷回data記錄,得到棧頭
memcpy(data, s->buffer + s->top*s->typesize, s->typesize);
//成功則return 1;
return 1;
}
int IsEmpty(stack * s){
return s->top == -1;//如果top為-1則棧空
}
int IsFull(stack * s){
return s->top == s->max-1;//如果top為max-1則棧滿
}
//___________________________________________________________________________________________________________________________________________________
//2.定義一個cal類型,其中data存數時sign為IsDouble,存字符時,sign為Ischar
typedef struct {
double data;
char sign;
} cal;
//3.查找對應符號(找到則返回該符號下標)(找不到則說明該部分為數字返回-1)
int SearchCode(char ch){
char * code = "+-*/()@";//@為終止符,算式輸入結束
int index = 0;//
while (code[index]){
if (code[index] == ch) return index;
index++;
}
return -1;
}
//4.得到兩個符號間的優先級
//與SearchCode相對應,
char GetPriority(char ch, char next){
//創建一個perferen表,第i行(列)對應SearchCode函數中code中的第i個字符
char perferen[7][7] = {
">><<<>>",
">><<<>>",
">>>><>>",
">>>><>>",
"<<<<<=E",
">>>>E>>",
"<<<<<E="
};
//找到兩個形參對應的字符
int c = SearchCode(ch);
int n = SearchCode(next);
//如果找不到對應運算符(字符不是運算符而是為數字)return E
if (c==-1 || n==-1) return 'E';
//如果找到兩個對應運算符則按照優先級表返回兩個運算符的優先級
return perferen[c][n];
}
//5.四則運算
double add(double a, double b) { return a+b; }
double sub(double a, double b) { return a-b; }
double mul(double a, double b) { return a*b; }
double ddiv(double a, double b) { return a/b; }
//整合四種運算
double calcu(double a, char ch, double b){
double (*calculation[4])(double,double) = {add,sub,mul,ddiv};
return calculation[SearchCode(ch)](a,b);
}
//6.檢測字符串
int CheckStr(char * buffer){
int n;
//遍歷字符串確保算式中無非法字符若檢測到非法字符return 0,若都合法則return 1
for (n = 0;buffer[n];n++){
if ((SearchCode(buffer[n]) != -1 || buffer[n] == '.' || (buffer[n] >= '0' && buffer[n] <= '9')) && buffer[n] != '@') continue;
else return 0;
}
buffer[n] = '@';//加上終止符,表示算式結束
buffer[n+1] = '\0';
return 1;
}
//7.得到數據轉化為double類型存入rs
int GetDigit(char * buffer, int * n, double * rs){
char str[30];
int i,j = 0;
for (i = 0;SearchCode(buffer[*n]) == -1;i++){
str[i] = buffer[*n];//從*n位置開始,將這一串數字字符存入str
(*n)++;
}
str[i] = '\0';
for (i = 0;str[i];i++){
if (str[i] == '.') j++;
}
//如果一段小數有多個小數點或小數點在數字首尾,return 0
if (j>1 || str[i-1] == '.' || str[0] == '.') return 0;
//rs接收轉化為double的數據
*rs = atof(str);
//操作成功return 1
return 1;
}
//8.將用戶輸入的buffer字符串轉化為可供程序運算的calstr數組
int resolu(char * buffer, cal * calstr){
int i = 0, j = 0;
cal c;
while (buffer[i]){
if (SearchCode(buffer[i]) == -1){
//如果得到數據不成功則return 0
if (GetDigit(buffer,&i, &c.data) == 0) return 0;
//如果成功得到數據則在c.sign標記為浮點數
c.sign = IsDouble;
//將c存入數組calstr中
calstr[j++] = c;
}
else{
//若符號為運算符
//判斷正負號
if (buffer[i] == '-' && (buffer[i-1] == '('||buffer[i-1] == '+'||buffer[i-1] == '-'||buffer[i-1] == '*'||buffer[i-1] == '/') || (i==0 && buffer[0] == '-')){
i++;
if (GetDigit(buffer,&i, &c.data) == 0) return 0;//在符號的下一位開始查找,若找不到數字return 0
//否則,給數字取相反數,c.sign標記為浮點數,存入calstr中
c.data = 0 - c.data;
c.sign = IsDouble;
calstr[j++] = c;
} else
//如果是正號,與符號處理方式同理
if (buffer[i] == '+' && (buffer[i-1] == '('||buffer[i-1] == '+'||buffer[i-1] == '-'||buffer[i-1] == '*'||buffer[i-1] == '/') || (i==0 && buffer[0] == '+')){
i++;
if (GetDigit(buffer, &i, &c.data) == 0) return 0;
c.sign = IsDouble;
calstr[j++] = c;
}
else{
//如果不是正負號,則為運算符,先強制轉換為double類型存在c.data里,然后c.sign標記為char類型,存入calstr
c.data = (double)buffer[i++];
c.sign = IsChar;
calstr[j++] = c;
}
}
}
//操作蔡成功則return 1
return 1;
}
//9.計算出結果
int result(cal * calstr, double * rs){
stack * pst = CreateStack(100,sizeof(char));//運算符棧
stack * pnd = CreateStack(100,sizeof(double));//數據棧
double num1,num2;
int n = 0;
char ch = '@';
Push(pst, &ch);
//在轉換得到的calstr中遍歷直到終止符'@"
while(ch != '@' || !(calstr[n].sign == IsChar && (char)calstr[n].data == '@')){
//如果calstr的n位上是浮點數,則將這個data壓棧進入數據棧pnd中
if (calstr[n].sign == IsDouble){
Push(pnd, &(calstr[n].data));
n++;
}
//反之,如果是運算符,則要檢測優先級
else{
switch( GetPriority(ch, (char)calstr[n].data)){
//如果運算符優先級較小,則讓ch等於優先級大的符號並壓入符號棧pst中
case '<':
ch = (char)calstr[n].data;
Push(pst, &ch);
n++;
break;
//如果結果為等號,讓符號出棧暫存到ch中
case '=':
if (!Pop(pst, &ch)) return 0;
n++;
break;
//如果ch優先級較高,則將前兩個數字及運算符出棧,分別儲存至num2,ch,num1中,進行運算,得到的結果再次壓棧進入pnd中
case '>':
if (!(Pop(pnd,&num2) && Pop(pst,&ch) && Pop(pnd,&num1))) return 0;
num1 = calcu(num1,ch,num2);
Push(pnd, &num1);
break;
//如果符號順序出錯,return 0
case 'E':
return 0;
}
}
//檢測是否可以得到棧頂符號,棧空則return 0
if (!GetTop(pst, &ch)) return 0;
}
//如果棧中得到了最終結果,並且取出pnd中的最終結果到rs,return 1
if (GetSize(pnd) == 1 && GetTop(pnd,rs)){
DestroyStack(pst);
DestroyStack(pnd);
return 1;
}
//否則 return 0
else{
return 0;
}
}
//10.用戶交互函數
void treatment()
{
char buffer[100];//用戶輸入的字符串(算式)
cal calstr[50];//計算用的數組
double rs = 0;//計算結果
printf("Enter your equation:");
gets(buffer);//讓用戶輸入算式buffer
//用戶不輸入"exit"就不退出
while (!(buffer[0]=='e' && buffer[1]=='x' && buffer[2]=='i' && buffer[3]=='t')){
//檢查buffer中字符君合法,成功將buffer轉化為用於計算的calstr數組,成功計算出結果存入rs
if (CheckStr(buffer) && resolu(buffer,calstr) && result(calstr,&rs)){
printf("\n%lf\n",rs);
}else{
printf("\nError!\n");
}
printf("Enter \"exit\"to quit");
printf("\nEnter your equation:");
gets(buffer);//再次讓用戶輸入算式
}
printf("\nbye\n");
}
//11.主函數
int main(){
treatment();
}