首页 > 其他分享 >【C语言小型项目实践】:初学者到中级水平的计算器项目

【C语言小型项目实践】:初学者到中级水平的计算器项目

时间:2024-10-09 09:19:06浏览次数:12  
标签:return ops double 计算器 C语言 初学者 expr stack 表达式

目录

一、计算器项目

1.1. 功能描述

1.2. 技术要点

 二、代码实现

2.1. 代码说明

2.2. 注意事项

 三、测试用例

3.1. 基本运算

3.2. 括号与优先级

3.3. 多个运算符

3.4. 更复杂的表达式

3.5. 错误情况

2.6. 带负数的运算

2.7. 混合情况


一、计算器项目

1.1. 功能描述

  • 此计算器项目旨在实现加、减、乘、除等基本数学运算,并支持括号以处理运算的优先级。用户可以通过命令行界面输入表达式,程序将解析并计算结果。

1.2. 技术要点

1. 用户输入读取

  • 使用scanffgets等函数从标准输入读取用户输入的表达式。
  • 注意处理输入中的空格和非法字符。

2. 表达式解析

  • 编写解析器,使用栈结构处理括号和运算符的优先级。
  • 可以考虑使用两个栈,一个用于存储操作数,另一个用于存储运算符。

3. 运算处理

  • 使用if-elseswitch语句根据运算符执行相应的数学运算。
  • 确保处理除数为零的情况,避免程序崩溃。

4. 连续计算

  • 使用循环结构,允许用户输入多个表达式并连续计算结果。
  • 提供退出选项,让用户能够随时结束程序。

5. 错误处理

  • 添加错误处理逻辑,处理无效输入和运算错误。
  • 向用户显示清晰的错误信息。

6. 界面设计(可选):

  • 对于更高级的用户,可以考虑设计一个简单的命令行界面,提供用户友好的提示和选项。

通过实现这个计算器项目,C语言初学者可以巩固基础知识,如变量声明、循环结构、条件语句和函数调用。同时,这个项目也提供了学习数据结构(如栈)和算法(如表达式解析)的机会,有助于提升编程能力和问题解决能力。

 二、代码实现

实现一个完整的支持括号和运算符优先级的计算器项目,包括表达式解析和栈处理,是一个相对复杂的任务。以下是一个简化的C语言代码框架。请注意,这个框架不会提供完整的错误处理和所有细节,但它将提供一个起点。

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include <math.h>

#define MAX_EXPR_SIZE 100

// 定义栈结构
typedef struct {
    double data[MAX_EXPR_SIZE];
    int top;
} Stack;

// 初始化栈
void initStack(Stack *stack) {
    stack->top = -1;
}

// 判断栈是否为空
int isEmpty(Stack *stack) {
    return stack->top == -1;
}

// 入栈
void push(Stack *stack, double value) {
    if (stack->top < MAX_EXPR_SIZE - 1) {
        stack->data[++stack->top] = value;
    } else {
        printf("Stack overflow\n");
        exit(EXIT_FAILURE);
    }
}

// 出栈
double pop(Stack *stack) {
    if (!isEmpty(stack)) {
        return stack->data[stack->top--];
    } else {
        printf("Stack underflow\n");
        exit(EXIT_FAILURE);
    }
}

// 获取栈顶元素
double peek(Stack *stack) {
    if (!isEmpty(stack)) {
        return stack->data[stack->top];
    } else {
        printf("Stack is empty\n");
        exit(EXIT_FAILURE);
    }
}

// 运算符优先级
int precedence(char op) {
    switch (op) {
        case '+':
        case '-':
            return 1;
        case '*':
        case '/':
            return 2;
        case '(':
        case ')':
            return 0;
    }
    return -1;
}

// 执行运算
double applyOp(double a, double b, char op) {
    switch (op) {
        case '+': return a + b;
        case '-': return a - b;
        case '*': return a * b;
        case '/':
            if (b == 0) {
                printf("Error: Division by zero\n");
                exit(EXIT_FAILURE);
            }
            return a / b;
    }
    return 0;
}

// 解析并计算表达式
double evaluate(char *expr) {
    Stack values;
    Stack ops;
    initStack(&values);
    initStack(&ops);

    for (int i = 0; expr[i] != '\0'; i++) {
        // 跳过空格
        if (isspace(expr[i])) {
            continue;
        }

        // 当前字符是数字或小数点
        if (isdigit(expr[i]) || expr[i] == '.') {
            double val = 0;
            // 处理多位数和小数
            while (isdigit(expr[i]) || expr[i] == '.') {
                val = val * 10 + (expr[i] - '0');
                if (expr[i] == '.') {
                    val /= 10;
                    while (isdigit(expr[++i])) {
                        val = val * 10 + (expr[i] - '0') / pow(10, (i - (strrchr(expr, '.') - expr + 1)));
                    }
                    i--; // 回到当前处理的字符位置
                }
                i++;
            }
            i--; // 回到当前处理的字符位置的前一个位置(为了for循环的i++)
            push(&values, val);
        }
        // 当前字符是左括号
        else if (expr[i] == '(') {
            push(&ops, expr[i]);
        }
        // 当前字符是右括号
        else if (expr[i] == ')') {
            while (!isEmpty(&ops) && peek(&ops) != '(') {
                double val2 = pop(&values);
                double val1 = pop(&values);
                char op = pop(&ops);
                push(&values, applyOp(val1, val2, op));
            }
            // 弹出左括号
            if (!isEmpty(&ops)) {
                pop(&ops);
            } else {
                printf("Error: Mismatched parentheses\n");
                exit(EXIT_FAILURE);
            }
        }
        // 当前字符是运算符
        else if (expr[i] == '+' || expr[i] == '-' || expr[i] == '*' || expr[i] == '/') {
            while (!isEmpty(&ops) && precedence(peek(&ops)) >= precedence(expr[i])) {
                double val2 = pop(&values);
                double val1 = pop(&values);
                char op = pop(&ops);
                push(&values, applyOp(val1, val2, op));
            }
            push(&ops, expr[i]);
        } else {
            printf("Error: Invalid character '%c' in expression\n", expr[i]);
            exit(EXIT_FAILURE);
        }
    }

    // 弹出所有剩余的操作符
    while (!isEmpty(&ops)) {
        double val2 = pop(&values);
        double val1 = pop(&values);
        char op = pop(&ops);
        push(&values, applyOp(val1, val2, op));
    }

    // 返回栈中唯一的值
    return pop(&values);
}

int main() {
    char expr[MAX_EXPR_SIZE];

    printf("Simple Calculator\n");
    printf("Enter expressions (type 'exit' to quit):\n");

    while (1) {
        printf("> ");
        fgets(expr, MAX_EXPR_SIZE, stdin);

        // 移除换行符
        expr[strcspn(expr, "\n")] = 0;

        // 检查是否输入 'exit'
        if (strcmp(expr, "exit") == 0) {
            break;
        }

        // 计算表达式
        double result = evaluate(expr);

        // 输出结果
        printf("Result: %.2f\n", result);
    }

    return 0;
}

2.1. 代码说明

  • 栈结构:用于存储操作数和运算符。
  • 函数
    • initStack:初始化栈。
    • isEmpty:检查栈是否为空。
    • push:入栈操作。
    • pop:出栈操作。
    • peek:获取栈顶元素。
    • precedence:返回运算符的优先级。
    • applyOp:根据运算符执行相应的运算。
    • evaluate:解析并计算表达式。
  • 主函数
    • 提供一个简单的命令行界面。
    • 读取用户输入的表达式。
    • 调用 evaluate 函数计算结果。
    • 输出结果。
    • 支持输入 exit 退出程序。

2.2. 注意事项

  • 程序处理多位数和小数。
  • 程序处理带负数的运算。
  • 程序在发生错误时输出错误信息并退出。
  • 程序使用 fgets 读取输入,可以处理空格和非法字符。

 三、测试用例

为了确保上面的代码能够正确运行并处理各种表达式,我们可以设计一系列测试用例。这些测试用例将涵盖不同的运算符、括号使用、优先级、以及可能的错误情况。以下是一些测试用例的示例:

3.1. 基本运算

用例:

  • 3 + 5 => 结果: 8.00
  • 10 - 4 => 结果: 6.00
  • 6 * 7 => 结果: 42.00
  • 24 / 6 => 结果: 4.00

3.2. 括号与优先级

  • 3 + (2 * 4) => 结果: 11.00
  • (10 - 4) * 2 => 结果: 12.00
  • 6 / (2 + 1) => 结果: 2.00

​​​​​​​

3.3. 多个运算符

  • 2 + 3 - 5 => 结果: 0.00
  • 4 * 5 / 2 => 结果: 10.00
  • 10 - 2 + 3 * 4 => 结果: 20.00(注意运算优先级)

3.4. 更复杂的表达式

  • ((3 + 5) * 2) - 8 => 结果: 8.00
  • 10 - (2 + 3) * 2 => 结果: 0.00(注意括号内的优先级)
  • 6 * (10 - (4 + 2)) => 结果: 24.00

3.5. 错误情况

  • 10 / 0 => 错误: 除数不能为零

​​​​​​​​​​​​​​

  • 3 + => 错误: 无效的表达式(缺少操作数)

  • * 4 => 错误: 无效的表达式(缺少操作数)

  • 3 + (2 * => 错误: 无效的表达式(缺少右括号和第二个操作数)

  • => 错误: 无效的表达式(空字符串)

2.6. 带负数的运算

  • -3 + 5 => 结果: 2.00
  • 10 - -4 => 结果: 14.00
  • -6 * -7 => 结果: 42.00

2.7. 混合情况

  • 3 + 2 * (4 - 1) => 结果: 9.00
  • (3 + 5) * (2 - 8) / -3 => 结果: 4.00

注意事项

  • 在输入测试用例时,确保按照程序的提示进行输入,避免额外的空格或字符导致解析错误。
  • 对于错误情况,程序应该能够优雅地处理并输出相应的错误信息,而不是崩溃或产生未定义行为。
  • 可以考虑增加更多的边界测试用例,如最大长度的表达式、包含特殊字符的表达式等,以进一步验证程序的健壮性。

标签:return,ops,double,计算器,C语言,初学者,expr,stack,表达式
From: https://blog.csdn.net/weixin_37800531/article/details/142748616

相关文章

  • Linux系统编程—I/O缓冲区(C语言实现)
    I/O缓冲区进程的I/O缓冲区机制是计算机操作系统中一个重要的概念,它涉及到数据在内存和外设之间的传输。以下是关于进程的I/O缓冲区机制的详细解释:1.定义与作用定义:I/O缓冲区是指在内存里开辟的一块区域,用来存放接收用户输入和用于计算机输出的数据,以减小系统开销和提高......
  • 【C语言】输出数据的二进制存储形式
        说在前面:是一个C语言新手,很新的新手,在这个专栏记录一些探索过程    今天学习中学到类型转换,将int和short类型赋值给char类型变量时,因为想要清楚看到隐式转换的结果,产生了写一个东西来输出数据在计算机中的二进制存储形式的想法,以下为尝试过程一、首先想......
  • C语言——static 关键字与 const 关键字
    static静态的        一、static修饰局部变量——称为静态局部变量                static改变了局部变量的生命周期(本质上是改变了变量的存储类型),当被static修饰时,局部变量由栈区存放到了静态区。voidtest(){intnum=1;printf("%d......
  • 实验2 C语言分支与循环基础应用编程-1
    任务一#include<stdio.h>#include<stdlib.h>#include<time.h>#defineN5#defineN1397#defineN2476#defineN321intmain(){intcnt;intrandom_major,random_no;srand(time(NULL));//以当前系统时间作为随机种子cnt=0;......
  • 初识C语言
    一、C语言的简介什么是C语言C语言之父C语言的由来C语言的发展为什么要学C语言/能做什么为什么要学习这套C语言课程内容介绍1、C语言标准1.1标准简史1972年C语⾔在⻉尔实验室诞⽣.丹尼斯·⾥奇参考B语⾔开发.1970-80年代,C语⾔被⼴泛应⽤,产⽣很多不同的C语⾔版本.程......
  • FTP连接池与多线程上传下载算法实现(C语言)
    FTP连接池与多线程上传下载算法实现(C语言)设计思路伪代码示例C代码示例为了避免多线程环境下FTP连接池在故障重连时导致的竞争条件和coredump问题,我们需要设计一个精细的连接池管理系统,确保在连接重连时,其他线程不会尝试使用该连接。以下是一个简化的设计思路和示......
  • c语言中的变量存储区域
    栈局部变量和函数参数通常存储在栈中。函数调用时,栈空间用于存储函数参数、返回地址和局部变量。intfunc(constchar*str1,char*str2,intcount){count++;printf("%s%s\n",str1,str2);returncount;}在这个函数中,参数,局部变量都是存储在栈上的,等函数返回......
  • c语言实用小程序
    ASCII编码程序:#include<stdio.h>Intmain(void){charx=?(num);printf(“%c”,x);return0;}解码:charx=’?(字符即字母或标点)’;print(“%d”,x);~//*typesize.c--打印类型大小*/#include<stdio.h>intmain(void){/*C99为类型大小提供%zd转换说明*/pri......
  • c语言模拟实现qsort
    要想模拟首先qsort函数首先我们应该知道这个函数的功能是什么接下来我为大家引入一个列子我们想要实现一组有序数的升序可以通过冒泡排序法思想是 两两相邻元素进行比较 代码如下 通过冒泡排序法 #define_CRT_SECURE_NO_WARNINGS#include<stdio.h>voidbubbl......
  • C语言初阶,猜数字游戏
    这是适合萌新练习的小程序,在了解了循环语句后可以实现game函数被调用后执行猜数字环节,直到猜对,也可以改变while的条件设置猜的次数  代码从这里开始,如果选择1则调用game函数  代码就分享到这里,谢谢大家!......