读书人

c语言简略科学计算器

发布时间: 2013-09-23 11:21:05 作者: rapoo

c语言简单科学计算器

算符优先法的思想。

#include <stdio.h>#include <stdlib.h>#include <ctype.h>#include <string.h>#include <math.h>#define MAX_TOKEN_LEN 100 //标记最大长度#define EXPR_INCREMENT 20//表达式长度的增量typedef struct {    double opnd; //操作数    char optr[11]; //运算符    int flag; //若为1,则为单目运算符,2则是双目运算符} SElemType; //栈元素类型typedef struct SNode { //栈    SElemType date;    struct SNode * next;} SNode, *Stack;struct { //用来存储一个操作数或运算符    char str[MAX_TOKEN_LEN];    int type; //类型,若为0,则为操作数,若为1则为运算符} token;struct { //expression,用来存储表达式    char *str;    int cur; //标记读取expr的当前位置}expr;Stack OPND, OPTR; //操作数栈operand,运算符栈operatorint expr_size; //表达式长度void InitStack(Stack *S) { //初始化栈    *S = (Stack)malloc(sizeof(SNode));    if(!(*S)) {        printf("动态申请内存失败!\n");        exit(0);    } //if    (*S)->next = NULL;} //InitStackvoid DestroyStack(Stack *S) { //销毁栈    SNode *p;    while(p = *S) {        *S = p->next;        free(p);    } //while} //DestroyStackvoid Push(Stack S, SElemType e) { //入栈    SNode *p;    p = (SNode *)malloc(sizeof(SNode));    if(! p) {        printf("动态申请内存失败!\n");        exit(0);    } //if    strcpy(p->date.optr,e.optr);    p->date.opnd = e.opnd;    p->date.flag = e.flag;    p->next = S->next;    S->next = p;} //Pushvoid Pop(Stack S,SElemType *e) { //出栈    SNode *p;    p = S->next;    if(! p) {        printf("栈为空,不能出栈!\n");        exit(0);    } //if    S->next = p->next;    strcpy(e->optr,p->date.optr);    e->opnd = p->date.opnd;    e->flag = p->date.flag;    free(p);} //Popvoid get_expr() { //获取expr字符串    char *p;    int size;    expr.cur = 0;    expr_size = 100;    expr.str = (char*)malloc(expr_size * sizeof(char));    if(! expr.str) {        printf("内存分配失败!\n");        exit(0);    } //if    size = 0;    p = expr.str;    while((*p = getchar()) != '\n') {        if(*p != ' ') {            if((*p >= 'A') && (*p <= 'Z')) {                *p = *p + 32; //将大写转换成小写            } //if            p++;            size++;            if(size == expr_size-1) { //此时expr.str已满,将其扩大                expr_size += EXPR_INCREMENT;                expr.str = (char*)realloc(expr.str,expr_size * sizeof(char));                if(! expr.str) {                    printf("内存分配失败!\n");                    exit(0);                } //if                p = &expr.str[size]; //realloc后需重新指定p            } //if        } //if    } //while    *p++ = '#';    *p = '\0';} //get_exprint IsOpnd(char ch) { //判断ch是否是操作数的一部分    if((ch >= '0') && (ch <= '9') || (ch == '.')) {        return 1;    } //if    if((ch == '-') || (ch == '+')) { //如若+-前面是'#'或'(',则为正负号        if((expr.cur == 0) || (expr.str[expr.cur-1] == '(')) {            return 1;        } //if    } //if    return 0;} //IsOpndvoid gettoken() { //获取一个标记    char *p = token.str;    *p = expr.str[expr.cur];    if(IsOpnd(*p)) {        while(IsOpnd(*++p = expr.str[++expr.cur])); //注意是分号结尾        *p = '\0';        token.type = 0; //将标记类型设定为操作数        return;    } //if        if((*p >= 'a') && (*p <= 'z')) { //接收sin,tan,ln之类的函数运算符或操作数        while((expr.str[expr.cur+1] >= 'a') && (expr.str[expr.cur+1] <= 'z')) {            *++p = expr.str[++expr.cur];        } //while    } //if    ++expr.cur;    *++p = '\0';        if(!strcmp(token.str,"e")) { //e为欧拉数,既自然对数的底数        sprintf(token.str,"%.16g",exp(1));//springf是库函数,功能是将显示在屏幕上的内容储存在字符串中        token.type = 0;        return;    } //if    if(!strcmp(token.str,"pi")) { //pi为圆周率        //sprintf(token.str,"%.16g",exp(1));//springf是库函数,功能是将显示在屏幕上的内容储存在字符串中        strcpy(token.str,"3.1415926535897932");        token.type = 0;        return;    } //if    token.type = 1; //将标记类型设定为运算符} //gettokenchar Precede(SElemType *optr1, SElemType *optr2) { //返回优先关系    char *str1,*str2;    str1 = optr1->optr;    str2 = optr2->optr;    if(!strcmp(str1,"ln") || !strcmp(str1,"lg") || !strcmp(str1,"sin") || !strcmp(str1,"cos") || !strcmp(str1,"tan")) {        optr1->flag = 1; //这些均为单目运算符        return (!strcmp(str2,"(") || !strcmp(str2,"^") || !strcmp(str2,"!")) ? '<' : '>';    } //if    if(!strcmp(str1,"!")) {        optr1->flag = 1;        return '>';    } //if    optr1->flag = 2;    switch(str1[0]) {        case '+': case '-': return (!strcmp(str2,"+") ||!strcmp(str2,"-") || !strcmp(str2,")") || !strcmp(str2,"#")) ? '>' : '<';        case '*': case '/': return (!strcmp(str2,"+") || !strcmp(str2,"-") ||!strcmp(str2,"*") ||                                    !strcmp(str2,"/") || !strcmp(str2,")") || !strcmp(str2,"#")) ? '>' : '<';        case '(': return !strcmp(str2,")") ? '=' : '<';        case ')': return  '>';        case '^': return (!strcmp(str2,"(") || !strcmp(str2,"!") || !strcmp(str2,"^")) ? '<' : '>';        case '#': return !strcmp(str2,"#") ? '=' : '<';    } //switch} //Precedelong factorial(long n) { //阶乘    return (n <= 1) ? 1 : n * factorial(n-1);} //factorialSElemType Operate(SElemType opnd1, SElemType optr, SElemType opnd2) { //计算    SElemType temp;    if(optr.flag == 1) {        if(!strcmp(optr.optr,"!")) temp.opnd = factorial((long)opnd2.opnd);        else if(!strcmp(optr.optr,"lg")) temp.opnd = log10(opnd2.opnd);        else if(!strcmp(optr.optr,"ln")) temp.opnd = log(opnd2.opnd);        else if(!strcmp(optr.optr,"sin")) temp.opnd = sin(opnd2.opnd);        else if(!strcmp(optr.optr,"cos")) temp.opnd = cos(opnd2.opnd);        else if(!strcmp(optr.optr,"tan")) temp.opnd = tan(opnd2.opnd);        return temp;    } //if    switch(optr.optr[0]) {        case '+': temp.opnd = opnd1.opnd + opnd2.opnd; break;        case '-': temp.opnd = opnd1.opnd - opnd2.opnd; break;        case '*': temp.opnd = opnd1.opnd * opnd2.opnd; break;        case '/': temp.opnd = opnd1.opnd / opnd2.opnd; break;        case '^': temp.opnd = pow(opnd1.opnd,opnd2.opnd);    } //switch    return temp;} //Operateint main() {    SElemType optr,opnd1,opnd2;    printf("\n欢迎使用简易科学计算器!\n");    printf("欧拉数为e,圆周率为pi,退出则输入quit!\n");    printf("优先级:括号高于'!'高于'^'高于lg,ln,sin,cos,tan高于*,/高于+,-\n");    printf("请输入计算表达式:\n\n");    while(1) {        get_expr();        if(!strcmp(expr.str,"quit#")) {            return 0;        } //if        InitStack(&OPTR);        InitStack(&OPND);        strcpy(optr.optr,"#");        Push(OPTR, optr);        gettoken(); //从expr.str中获取一个标记(操作数或运算符)        while(strcmp(token.str,"#") || strcmp(OPTR->next->date.optr,"#")) {            if(token.type) { //说明token存储的是运算符                strcpy(optr.optr,token.str);                switch(Precede(&(OPTR->next->date),&optr)) {                    case '<': //栈顶元素优先权低                        strcpy(optr.optr,token.str);                        Push(OPTR,optr);                        gettoken();                        break;                    case '=': //脱括号并接收下一字符                        Pop(OPTR,&optr);                        gettoken();                        break;                    case '>': //退栈并将运算结果入栈                        Pop(OPTR,&optr);                        Pop(OPND,&opnd2);                        if(optr.flag == 2) { //是双目运算符才需另一个操作符                            Pop(OPND,&opnd1);                        } //if                        Push(OPND,Operate(opnd1,optr,opnd2));                } //switch            } //if            else { //说明token存储的是操作数                opnd1.opnd = atof(token.str);                Push(OPND,opnd1);//atof将token.str转换为双精度数                gettoken(); //获取下一个标记            } //else        } //while        printf("%.16g\n\n",OPND->next->date.opnd);        free(expr.str);        DestroyStack(&OPTR);        DestroyStack(&OPND);    } //while    free(expr.str);    return 0;} //main


读书人网 >C语言

热点推荐