目录
一、计算器项目
1.1. 功能描述
- 此计算器项目旨在实现加、减、乘、除等基本数学运算,并支持括号以处理运算的优先级。用户可以通过命令行界面输入表达式,程序将解析并计算结果。
1.2. 技术要点
1. 用户输入读取:
- 使用
scanf
或fgets
等函数从标准输入读取用户输入的表达式。 - 注意处理输入中的空格和非法字符。
2. 表达式解析:
- 编写解析器,使用栈结构处理括号和运算符的优先级。
- 可以考虑使用两个栈,一个用于存储操作数,另一个用于存储运算符。
3. 运算处理:
- 使用
if-else
或switch
语句根据运算符执行相应的数学运算。 - 确保处理除数为零的情况,避免程序崩溃。
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注意事项
- 在输入测试用例时,确保按照程序的提示进行输入,避免额外的空格或字符导致解析错误。
- 对于错误情况,程序应该能够优雅地处理并输出相应的错误信息,而不是崩溃或产生未定义行为。
- 可以考虑增加更多的边界测试用例,如最大长度的表达式、包含特殊字符的表达式等,以进一步验证程序的健壮性。