当前位置:首页 > C > 正文

C语言栈式计算器(从零实现表达式求值的完整教程)

在学习数据结构的过程中,C语言栈式计算器是一个非常经典且实用的项目。它不仅能帮助你深入理解“栈”这种数据结构,还能掌握如何将中缀表达式转换为后缀表达式并进行求值。本教程将手把手带你用C语言实现一个支持加减乘除和括号的简易计算器,即使你是编程小白,也能轻松跟上!

C语言栈式计算器(从零实现表达式求值的完整教程) C语言栈式计算器 栈实现计算器 C语言表达式求值 后缀表达式计算 第1张

一、什么是栈?为什么用栈做计算器?

栈(Stack)是一种“后进先出”(LIFO, Last In First Out)的数据结构。你可以把它想象成一摞盘子:你只能从最上面拿盘子,也只能把新盘子放在最上面。

在表达式求值中,栈能高效地处理运算符优先级和括号嵌套问题。例如,当我们遇到 (3 + 5) * 2 时,必须先算括号内的内容。使用两个栈(一个存数字,一个存运算符),我们可以轻松实现这一逻辑。

二、核心思路:中缀转后缀 + 后缀求值

我们日常写的表达式如 3 + 5 * 2中缀表达式(运算符在中间)。但计算机更喜欢后缀表达式(也叫逆波兰表达式),比如 3 5 2 * +

因此,我们的计算器分为两步:

  1. 将中缀表达式转换为后缀表达式(使用运算符栈
  2. 对后缀表达式进行求值(使用操作数栈

三、完整代码实现

下面是一个完整的 C 语言栈式计算器代码,支持整数、四则运算和括号:

#include <stdio.h>#include <stdlib.h>#include <string.h>#include <ctype.h>#define MAX 100// 数字栈typedef struct {    int data[MAX];    int top;} NumStack;// 运算符栈typedef struct {    char data[MAX];    int top;} OpStack;// 初始化栈void initNumStack(NumStack *s) { s->top = -1; }void initOpStack(OpStack *s) { s->top = -1; }// 入栈、出栈(数字)void pushNum(NumStack *s, int val) { s->data[++s->top] = val; }int popNum(NumStack *s) { return s->data[s->top--]; }// 入栈、出栈(运算符)void pushOp(OpStack *s, char op) { s->data[++s->top] = op; }char popOp(OpStack *s) { return s->data[s->top--]; }// 判断是否为空int isEmptyNum(NumStack *s) { return s->top == -1; }int isEmptyOp(OpStack *s) { return s->top == -1; }// 获取栈顶char peekOp(OpStack *s) { return s->data[s->top]; }// 判断运算符优先级int precedence(char op) {    switch(op) {        case '+':        case '-': return 1;        case '*':        case '/': return 2;        default: return 0;    }}// 中缀转后缀并求值int evaluate(char expr[]) {    NumStack nums;    OpStack ops;    initNumStack(&nums);    initOpStack(&ops);    for (int i = 0; expr[i] != '\0'; i++) {        if (isspace(expr[i])) continue;        // 如果是数字        if (isdigit(expr[i])) {            int num = 0;            while (isdigit(expr[i])) {                num = num * 10 + (expr[i] - '0');                i++;            }            i--; // 回退一位            pushNum(&nums, num);        }        // 如果是左括号        else if (expr[i] == '(') {            pushOp(&ops, expr[i]);        }        // 如果是右括号        else if (expr[i] == ')') {            while (!isEmptyOp(&ops) && peekOp(&ops) != '(') {                char op = popOp(&ops);                int b = popNum(&nums);                int a = popNum(&nums);                switch(op) {                    case '+': pushNum(&nums, a + b); break;                    case '-': pushNum(&nums, a - b); break;                    case '*': pushNum(&nums, a * b); break;                    case '/': pushNum(&nums, a / b); break;                }            }            popOp(&ops); // 弹出'('        }        // 如果是运算符        else if (expr[i] == '+' || expr[i] == '-' ||                  expr[i] == '*' || expr[i] == '/') {            while (!isEmptyOp(&ops) && peekOp(&ops) != '(' &&                   precedence(peekOp(&ops)) >= precedence(expr[i])) {                char op = popOp(&ops);                int b = popNum(&nums);                int a = popNum(&nums);                switch(op) {                    case '+': pushNum(&nums, a + b); break;                    case '-': pushNum(&nums, a - b); break;                    case '*': pushNum(&nums, a * b); break;                    case '/': pushNum(&nums, a / b); break;                }            }            pushOp(&ops, expr[i]);        }    }    // 处理剩余运算符    while (!isEmptyOp(&ops)) {        char op = popOp(&ops);        int b = popNum(&nums);        int a = popNum(&nums);        switch(op) {            case '+': pushNum(&nums, a + b); break;            case '-': pushNum(&nums, a - b); break;            case '*': pushNum(&nums, a * b); break;            case '/': pushNum(&nums, a / b); break;        }    }    return popNum(&nums);}int main() {    char expr[100];    printf("请输入一个表达式(支持+ - * / 和括号):\n");    fgets(expr, sizeof(expr), stdin);    expr[strcspn(expr, "\n")] = 0; // 去掉换行符    int result = evaluate(expr);    printf("结果是:%d\n", result);    return 0;}

四、代码说明与关键点

  • 双栈设计:一个栈存数字(NumStack),一个栈存运算符(OpStack)。
  • 优先级处理:通过 precedence() 函数判断 * / 的优先级高于 + -。
  • 括号匹配:遇到 ')' 就不断弹出运算符直到 '('。
  • 边转换边计算:本实现没有显式生成后缀表达式字符串,而是在扫描中缀表达式的同时直接计算,效率更高。

五、测试示例

运行程序后,输入以下表达式:

(3 + 5) * 2 - 10 / 2

输出结果应为:

结果是:11

六、总结

通过这个项目,你不仅掌握了 C语言表达式求值 的核心算法,还深入理解了栈在实际问题中的应用。这也是面试中常见的编程题之一。

如果你希望扩展功能(如支持小数、负数、更多函数),可以在当前框架上继续开发。记住,后缀表达式计算 是很多高级计算器和编译器的基础技术。

现在,你已经拥有了一个自己动手实现的 C语言栈式计算器!快去试试不同的表达式吧!