C语言实现简易计算器(可作加减乘除)
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();
}