C 语言综合练习二:命令行计算器
更新时间:2026-08-26。本文是
languages/c/主题入门层第 73 篇。练习二上难度:做一个命令行计算器,支持2 + 3、10 * 4.5这类输入。它同时锻炼函数封装(每个运算一个函数)、switch 分支、字符串处理(拆分运算符和数字)和边界处理(除零、非法运算符)。代码已入库demos/c-beginner-practice/calculator.c。
本文要回答的问题
- 怎么解析"数字 运算符 数字"这种输入?
- 运算符分发用 if 还是 switch?
- 除零、非法输入怎么优雅处理?
一、需求拆解
$ ./calculator 10 + 3.5
10 + 3.5 = 13.5
$ ./calculator 5 / 0
错误:不能除以零输入来自 命令行参数(argc/argv):argv[1] 第一个数字、argv[2] 运算符、argv[3] 第二个数字。
| 模块 | 手段 |
|---|---|
| 参数解析 | argc/argv + strtod(带错误检查) |
| 运算符分发 | switch |
| 运算实现 | 每类一个函数 |
| 错误处理 | 参数数量不足、除零、非法运算符 |
二、完整代码
c
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
double add(double a, double b) { return a + b; }
double sub(double a, double b) { return a - b; }
double mul(double a, double b) { return a * b; }
double divide(double a, double b) { return a / b; }
int main(int argc, char *argv[]) {
if (argc != 4) {
fprintf(stderr, "用法: %s 数字 运算符 数字\n", argv[0]);
fprintf(stderr, "例: %s 10 + 3.5\n", argv[0]);
return 1;
}
char *end1, *end2;
errno = 0;
double a = strtod(argv[1], &end1);
double b = strtod(argv[3], &end2);
if (*end1 != '\0' || *end2 != '\0') {
fprintf(stderr, "错误:操作数必须是数字\n");
return 1;
}
double result;
switch (argv[2][0]) {
case '+': result = add(a, b); break;
case '-': result = sub(a, b); break;
case '*': result = mul(a, b); break;
case '/':
if (b == 0.0) {
fprintf(stderr, "错误:不能除以零\n");
return 1;
}
result = divide(a, b);
break;
default:
fprintf(stderr, "错误:不支持的运算符 %s\n", argv[2]);
return 1;
}
printf("%g %s %g = %g\n", a, argv[2], b, result);
return 0;
}三、关键设计点
1. strtod 检查非法数字:
c
char *end;
double a = strtod("abc", &end);
// end 指向 'a',*end != '\0' → 输入非法用 *end != '\0' 判断是否整个字符串都被消费了(stdlib 转换 讲过)。
2. 除零在调用前拦截:
c
case '/':
if (b == 0.0) { ... return 1; } // 先检查再调用
result = divide(a, b);浮点除零在 C 里是 UB(可能产生 inf 或崩溃),自己拦掉。
3. switch 分发运算符:4 个分支清晰,default 兜底非法运算符。
四、运行演示
$ make run ARGS="10 + 3.5"
10 + 3.5 = 13.5
$ ./calculator 5 / 0
错误:不能除以零
$ ./calculator 2 x 3
错误:不支持的运算符 x
$ ./calculator 1 2
用法: ./calculator 数字 运算符 数字五、扩展练习
- 支持
%取余(注意整数); - 支持
sqrt、pow单目运算(./calculator sqrt 16); - 用函数指针数组替代 switch(回调的分发思想)。
六、一句话总结
c
/*
* calculator.c —— C 语言入门综合练习二:命令行计算器
* 文档: languages/c/beginner/73-practice-calculator.md
* 编译: make && ./calculator 10 + 3.5
*/
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
double add(double a, double b) { return a + b; }
double sub(double a, double b) { return a - b; }
double mul(double a, double b) { return a * b; }
double divide(double a, double b) { return a / b; }
int main(int argc, char *argv[]) {
if (argc != 4) {
fprintf(stderr, "用法: %s 数字 运算符 数字\n", argv[0]);
fprintf(stderr, "例: %s 10 + 3.5\n", argv[0]);
return 1;
}
char *end1, *end2;
errno = 0;
double a = strtod(argv[1], &end1);
double b = strtod(argv[3], &end2);
if (*end1 != '\0' || *end2 != '\0') {
fprintf(stderr, "错误:操作数必须是数字\n");
return 1;
}
double result;
switch (argv[2][0]) {
case '+': result = add(a, b); break;
case '-': result = sub(a, b); break;
case '*': result = mul(a, b); break;
case '/':
if (b == 0.0) {
fprintf(stderr, "错误:不能除以零\n");
return 1;
}
result = divide(a, b);
break;
default:
fprintf(stderr, "错误:不支持的运算符 %s\n", argv[2]);
return 1;
}
printf("%g %s %g = %g\n", a, argv[2], b, result);
return 0;
}与本站主线衔接
- 字符串转数字,见stdlib.h 字符串转换;
- switch 分支,见switch-case;
- 函数指针分发(进阶方向),见函数指针。