如何为K&R教材的C语言RPN计算器添加负数处理能力
RPN计算器负数支持实现方案
你之前的修改方向不对:首先ggetch本身不会跳过空格,跳空格的是ggetop开头专门写的跳过空格、制表符的循环。你在主函数的case '-'分支处理的话,所有'-'已经被ggetop判定为运算符返回了,根本拿不到后面的数字内容,自然实现不了负数识别。
核心思路:把负号的判断逻辑放到ggetop函数里处理,而不是在switch分支里——需要在ggetop读到'-'的瞬间,结合上下文判断它是二元减号,还是属于数字的负号位。
前置说明
如果要在'-'和后面的数字之间加空格,会天然产生歧义:输入1 - 2到底是计算1减2,还是输入1和-2两个数?所以常规RPN计算器的负数规则是负号和后面的数字之间不能有空格,这样歧义自然消除,也不需要在跳完空格后反复回退判断。
你之前以为“ggetop和ggetch会自动跳过空格”导致失败,本质是ggetop读到'-'就直接当成运算符返回了,根本没有继续读取后面跟的数字内容。
具体修改步骤
1. 新增上下文标记
加一个全局变量记录上一次读到的token类型,用来判断当前位置是不是应该读新操作数的场景:
// 初始值为-1,代表程序刚启动,还没读过任何token int last_type = -1;
2. 重构ggetop的负号判断逻辑
原来的ggetop读到非数字/非小数点的字符就直接返回,现在调整为读到'-'时先做判断:
- 先读下一个字符,如果下一个字符是数字、或者小数点,再检查当前是否处于“读操作数”的上下文(程序刚启动、上一个token是运算符/换行符)
- 满足条件的话,把'-'连同后面的数字部分一起存入字符串,最终返回
NUMBER标记,走数字入栈逻辑 - 不满足条件的话,把刚才多读的字符通过
unggetch放回缓冲区,直接返回'-'作为减号运算符
3. 修正原有bug
你之前在case '-'分支的写法存在明显错误:
- 连续调用两次
ggetch(),等于读走了两个字符,根本不是只判断'-'后面的第一个字符 - ggetop返回'-'的时候,传入的字符串
s只存了'-'本身,没有存后面的数字,调用aatof(s)得到的是-0,完全不符合预期 - 代码里写的
printf("%s",6)是严重错误,%s格式符要求传入字符串指针,传整数6会直接触发内存访问错误导致程序崩溃
参考核心修改代码
修改后的ggetop函数完整实现:
int ggetop(char s[]) { int i, c; // 保留原有跳过空格、制表符的逻辑 while ((s[0] = c = ggetch()) == ' ' || c == '\t') ; s[1] = '\0'; // 新增:读到'-'时先判断是不是负号 if (c == '-') { int next_c = ggetch(); // 满足负号条件:下一个是数字/小数点,且当前是该读操作数的上下文 if ( (isdigit(next_c) || next_c == '.') && (last_type == -1 || last_type == '+' || last_type == '-' || last_type == '*' || last_type == '/' || last_type == '%' || last_type == '\n') ) { i = 0; s[i++] = c; c = next_c; // 沿用原有逻辑读取后面的数字、小数部分 while (isdigit(s[i++] = c = ggetch())) ; if (c == '.') while (isdigit(s[i++] = c = ggetch())) ; s[--i] = '\0'; if (c != EOF) unggetch(c); last_type = NUMBER; return NUMBER; } else { // 不是负号,把多读的字符放回缓冲区,返回'-'作为减号 unggetch(next_c); last_type = '-'; return '-'; } } // 保留原有非数字字符的返回逻辑 if (!isdigit(c) && c != '.') { last_type = c; return c; } // 保留原有数字读取逻辑 i = 0; if (isdigit(c)) while (isdigit(s[++i] = c = ggetch())) ; if (c == '.') while (isdigit(s[++i] = c = ggetch())) ; s[i] = '\0'; if (c != EOF) unggetch(c); last_type = NUMBER; return NUMBER; }
主循环里的case '-'分支改回标准的二元减号逻辑即可,不需要额外处理:
case '-': op2 = pop(); push(pop() - op2); break;
运行验证
修改完成后,以下输入均可正常计算:
- 输入
-123 4 +→ 输出-119 - 输入
3 -4 *→ 输出-12 - 输入
10 -3 %→ 输出1 - 输入
5 3 -→ 输出2
内容的提问来源于stack exchange,提问作者Autumn Exp1
相关产品推荐
相关产品推荐

