简单计算器(c实现)

设计要求:


输入一个只含有+、-、*、/、和%运算符的数学表达式,然后计算出其值;并且要能够处理圆括号。
例如:2 + 3 * (4 - 2)


分析过程:


  • 我们都知道后缀表达式是非常符合计算机的计算方式的,因为它不用考虑优先级的问题,只需要从左到右依次计算即可。
  • 所以我们的整体思路是:先把输入的中缀表达式转换为后缀表达式,然后再计算出它的值。具体过程如下:
下面是程序的具体流程:

  • 每次调用exp_trans函数读取一行输入,将其中的中缀表达式转换为后缀表达式,并保存在全局数组expr中;而getop函数则每次从expr中读取字符,解析生成的后缀表达式,最后借用stack计算出它的值。
  • 为了方便计算后缀表达式,我们在解析生成的后缀表达式时对其格式作了一点简单的调整,即在每个运算符或操作数之间都加上了空格。
  • 还应当注意的是:浮点数不能直接和0进行比较。

计算后缀表达式的算法是这样的:
while (下一个运算符或操作数不是EOF)
    if (是数)
        将该数压入到栈中
    else if (是运算符)
        弹出所需数目的操作数
    执行运算
        将结果压入到栈中
    else if (是\n)
        弹出并打印栈顶值
    else
        出错

下面给出代码实现:

首先是一些声明

#define MAXEXP 200          /*  允许输入的中缀表达式的最大长度  */
#define MAXOP  20           /*  运算符或操作数的最大长度  */
#define NUMBER '0'          /*  标识找到一个数  */

extern int expr[];
extern int li;

int getop(char *);
void exp_trans(void);
int op_prior(int);

void push(double);
double pop(void);
double top(void);
int isfull(void);
int isempty(void);

主函数主要控制表达式的计算,值得一提的是,这里便是switch语句的一个典型应用

main.c

int expr[MAXEXP];     /*  存储转换后的后缀表达式  */
int li = 0;    /*  expr数组的索引  */

int main(int argc, const char *argv[])
{
    char s[MAXOP];    /*  存储获取的运算符或操作数  */
    int type;
    double op2;

    while ((type = getop(s)) != EOF)
        switch (type) {
        case NUMBER:
            push(atof(s));
            break;
        case '+':
            push(pop() + pop());
            break;
        case '*':
            push(pop() * pop());
            break;
        case '-':
            op2 = pop();
            push(pop() - op2);
            break;
        case '/':
            op2 = pop();
            if (fabs(op2) > DBL_EPSILON)
                push(pop() / op2);
            else
                printf("error: zero divisor\n");
            break;
        case '%':
            op2 = pop();
            if (fabs(op2) > DBL_EPSILON)
                push(fmod(pop(), op2));
            else
                printf("error: zero divisor\n");
            break;
        case '\n':
            printf("\t%.8g\n", pop());
            break;
        default:
            printf("error: unknow command %s\n", s);
            break;
        }
    return 0;
}

然后exp_trans完成表达式的转换,转换过程也是利用栈来完成的,和最终计算表达式是用的相同的一个栈。在我的另一篇博客中有对转换过程的详细描述。

expts.c

/*
 *  遇见数字,输出;遇见操作符,入栈;遇见比栈顶操作符优先级更低的,弹出栈中比遇见的操作符优先级高的;
 *  遇见')',弹出栈中'('以前的操作符
 */

/*  exp_trans函数:将中缀表达式转换为后缀表达式   */
void exp_trans(void)
{
    int c, i;

    i = 0;
    while ((c = getchar()) != EOF)
        if (isspace(c) || isdigit(c) || c == '.') {        /*  原样保存空白符和数字  */
            if (c == '\n') {
                while (!isempty()) {        /*  遇见\n,弹出栈中所有操作符  */
                    expr[i++] = pop();
                    expr[i++] = ' ';
                }
                break;
            }
            expr[i++] = c;
        } else if (op_prior(c) || c == ')') {    /*  是运算符或')'  */
            if (isempty() && c != '-') {
                push(c);
            } else if (c == ')') {
                while (!isempty()) {
                    if (top() == '(') {
                        pop();
                        break;
                    }
                    expr[i++] = pop();
                    expr[i++] = ' ';
                }
            } else {
                if (c == '-') {       /*  判断是负号还是减号  */
                    if (isdigit(c = getchar())) {
                        expr[i++] = '-';
                        expr[i++] = c;
                        continue;
                    } else {
                        ungetc(c, stdin);
                        c = '-';
                    }
                }
                while (!isempty() && top() != '(' && op_prior(top()) >= op_prior(c)) {
                    expr[i++] = pop();
                    expr[i++] = ' ';
                }
                push(c);
            }
        }
    expr[i++] = c;
    expr[i] = '\0';
}

/*  op_prior函数:返回对应操作符的优先级   */
int op_prior(int c)
{
    switch (c) {
    case '+':
    case '-':
        return 1;
        break;
    case '*':
    case '/':
    case '%':
        return 2;
        break;
    case '(':
        return 3;
        break;
    default:
        return 0;
        break;
    }
}

我们每次调用exp_trans函数读取一行,然后getop函数用于从中解析需要的运算符和操作数

getop.c

/*  getop函数:获取下一个运算符或操作数    */
int getop(char *s)
{
    if (expr[li] == '\0') {
        exp_trans();    /* 如果刚开始时未读入或读到expr的末尾,就调用exp_trans函数读入新的一行  */
        li = 0;
    }
    while ((s[0] = expr[li]) == ' ' || expr[li] == '\t')
        li++;
    s[1] = '\0';
    if (!isdigit(expr[li]) && !islower(expr[li]) && expr[li] != '.' && expr[li] != '-')
        return expr[li++];

    if (expr[li] == '-') {      /*  判断‘-’是减号还是负号  */
        if (isdigit(expr[++li]) || expr[li] == '.')
            *++s = expr[li];
        else
            return '-';
    }     /*  收集数  */
    if (isdigit(expr[li]))
        while (isdigit(*++s = expr[++li]))
            ;
    if (expr[li] == '.')
        while (isdigit(*++s = expr[++li]))
            ;
    *s = '\0';
    return NUMBER;
}

最后就是栈的实现了,很简单,没什么好说的

#define MAXVAL 100

static int val[MAXVAL];    /*  值栈  */
static int sp = -1;    /*  栈顶指针  */

/*  push函数:将c压入栈中  */
void push(double c)
{
    if (!isfull())
        val[++sp] = c;
    else
        printf("stack is full\n");
}

/*  pop函数:弹出并返回栈顶值  */
double pop(void)
{
    if (!isempty())
        return val[sp--];
    else {
        printf("stack is empty\n");
        return 0;
    }
}

/*  top函数:只返回栈顶值  */
double top(void)
{
    if (!isempty())
        return val[sp];
    else {
        printf("stack is empty\n");
        return 0;
    }
}

/*  isfull函数:判断栈是否已满  */
int isfull(void)
{
    return sp == MAXVAL - 1;
}

/*  isempty函数:判断栈是否为空  */
int isempty(void)
{
    return sp == -1;
}

运行结果:

猜你喜欢

转载自blog.csdn.net/qq_41145192/article/details/80779039