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();
}

参考文献链接如下

[参考文献](c语言强化训练——简易计算器 - 石莹 - 博客园 (cnblogs.com))

posted @ 2021-09-10 11:39  Quirkygbl  阅读(3286)  评论(0编辑  收藏  举报