calc20 --- 魔法数修复、测试分隔符、位运算截断检查、进制溢出检查
1. 概述
本次迭代从 suggestions.md 中选取了 4 个待改进项进行修复,涵盖代码质量、测试框架和运行时健壮性三个方面。
修复项:
| 编号 | 问题 | 类型 |
|---|---|---|
| C1 | var_name[32] 魔法数 |
代码质量 |
| G3 | 测试文件分隔符与表达式分隔符 ; 冲突 |
测试框架 |
| G4 | 位运算截断 int 无警告 |
运行时健壮性 |
| G5 | 进制数溢出无检查 | 运行时健壮性 |
2. 变更清单
| 文件 | 操作 | 说明 |
|---|---|---|
main.c |
编辑 | var_name[32] → var_name[MAX_NAME](2 处) |
test.expr |
编辑 | 分隔符 ; → `````(反引号),避免与表达式分隔符 ; 冲突 |
Makefile |
编辑 | IFS=';' → IFS='\'` |
eval.c |
编辑 | 新增 #include <limits.h> + check_int_trunc() 函数;6 处 (int) 转换前添加截断检查 |
parse.c |
编辑 | 新增 #include <errno.h> + #include <stdio.h>;3 处 strtoul 后添加 errno == ERANGE 溢出检查 |
3. C1 --- var_name[32] 魔法数
3.1 改造前
c
char var_name[32];
硬编码的 32,与 parse.h 中 Token.name[32] 无显式关联。
3.2 改造后
c
char var_name[MAX_NAME];
MAX_NAME 在 parse.h 中定义为 32,两处统一引用,修改一端时同步更新。
4. G3 --- 测试文件分隔符
4.1 问题
test.expr 使用 ; 作为字段分隔符,但 ; 现在也是合法的表达式分隔符(表达式分隔功能),导致 1;2;3 等表达式无法在测试文件中使用。
4.2 方案
将分隔符改为反引号 `````,原因:反引号在 calc 的词法分析器中永远为非法字符(TOKEN_ERROR),不会与任何合法表达式冲突。
| 文件 | 改动 |
|---|---|
test.expr |
; → ````` |
Makefile |
IFS=';' → IFS='\'` |
同时保留了 ; 作为表达式分隔符的功能不变。
5. G4 --- 位运算截断检查
5.1 问题
eval.c 中位运算操作数先转为 int(32 位),超出 INT_MAX(2147483647)的值被静默截断,用户得不到提示。
5.2 方案
新增 check_int_trunc 函数,在 6 处 (int) 转换前调用:
c
static int check_int_trunc(long double v)
{
if (v > INT_MAX || v < INT_MIN) {
fprintf(stderr, " warning: value truncated to int\n");
return 1;
}
return 0;
}
调用位置:parse_bitor、parse_bitxor、parse_bitand、parse_shift(SHL/SHR)、parse_unary(BITNOT)。
6. G5 --- 进制数溢出检查
6.1 问题
parse.c 中 strtoul 解析十六进制/二进制/八进制数字时,超出 unsigned long 范围的值被静默截断为 ULONG_MAX,用户得不到提示。
6.2 方案
在每个 strtoul 调用前重置 errno = 0,调用后检查 errno == ERANGE:
c
errno = 0;
val = (long double)strtoul(input_pos, &end, 16);
if (errno == ERANGE)
fprintf(stderr, " warning: integer overflow\n");
7. 测试验证
bash
$ make test
============================================
calc --- 测试套件
============================================
122 passed, 0 failed, 122 total
============================================
全部 122 个回归测试通过,功能不变,新增的检查仅在溢出/截断时输出警告,不影响正常计算结果。