命令行表达式计算器开发遇无限循环问题求助
我来帮你搞定这个无限循环的问题,顺便聊聊输入处理的最优方案~
问题根源:指针越界+错误的内存释放
先拆解你代码里导致无限循环的核心问题:
- buf指针没有重置:你在处理字符时做了
buf += n和buf++,导致处理完一行后,buf指针已经跳到了\n甚至更后面的位置。下一次fgets读取新行时,buf还是停在这个偏移位置,*buf != '\n'的条件永远成立,直接陷入死循环。 - 错误的链表释放:你用
free(head)只释放了头节点,整个链表的其他节点会造成内存泄漏,而且下次循环时head还残留着旧地址,逻辑会混乱。 - 输入判断不完整:没有考虑输入超长被截断后没有
\n的情况,也没支持小数输入(比如12.5这种)。
输入处理的最优选择:fgets其实很合适
你不用换输入函数,fgets是处理行输入的安全选项——它能限制读取长度,避免缓冲区溢出,完全适配你“像Shell一样读一行算一行”的需求,只要正确管理指针位置就行。
修正后的代码及关键改动
下面是调整后的完整代码,我标注了核心改动点:
#include <stdio.h> #include <ctype.h> #include <stdlib.h> #include <math.h> #define MAX 100 struct n{ double value; char op; struct n *next; }; void print_list(struct n *head); void add_item(struct n **ptr, double *data); void free_all(struct n *head); void f(double x); int main() { struct n *head = NULL; char *buf = malloc(MAX * sizeof (*buf)); char *buf_start = buf; // 保存缓冲区初始地址,用于每次重置 int n; double num; for(;;) { head = NULL; // 处理新行前重置链表头 printf("> "); // 加个提示符,更像Shell交互 if(fgets(buf_start, MAX, stdin) == NULL) { free_all(head); free(buf_start); return 0; } buf = buf_start; // 每次处理新行,把buf指针重置到起始位置 // 同时判断是否到字符串末尾(应对输入超长截断的情况) while (*buf != '\n' && *buf != '\0') { // 支持小数输入,加入对'.'的判断 if (isdigit(*buf) || *buf == '.') { sscanf(buf, "%lf%n", &num, &n); add_item(&head, &num); buf += n; } else { // 这里后续可以扩展处理运算符,比如保存到节点的op字段 buf++; } } printf("当前链表存储的数字:"); print_list(head); printf("\n"); free_all(head); // 正确释放整个链表,避免内存泄漏 } free(buf_start); // 程序结束前释放缓冲区 return 0; } // 调整递归顺序,让数字正向输出(原代码是反向输出) void print_list(struct n *head) { if (head != NULL) { print_list(head->next); f(head->value); } } void add_item(struct n **ptr, double *data) { struct n *item = malloc(sizeof *item); item->value = *data; item->next = *ptr; item->op = '\0'; // 初始化为空,后续处理运算符时再赋值 *ptr = item; } void free_all(struct n *head) { struct n *tmp; while (head != NULL) { tmp = head; head = head->next; free(tmp); } } void f(double x) { double i, r = modf(x, &i); if(fabs(r)<.00001) printf("%.f ",i); else printf("%f ",x); }
关键改动说明:
- 新增
buf_start保存缓冲区初始地址,每次处理新行时重置buf指针,彻底解决无限循环问题 - 处理输入时增加
*buf != '\0'判断,覆盖输入超长被截断的场景 - 每次循环前重置
head = NULL,避免链表残留上一次的数据 - 把
free(head)替换为free_all(head),正确释放整个链表 - 调整
print_list的递归顺序,让数字按输入顺序正向输出 - 支持小数输入,增加对
.的判断 - 加入Shell风格的提示符
>,提升交互体验
额外优化建议
- 如果要处理运算符,可以在
else分支里判断+-*/^()等字符,把运算符保存到链表节点的op字段 - 递归实现的
print_list在链表过长时可能栈溢出,建议改成迭代版本:void print_list(struct n *head) { struct n *current = head; while (current != NULL) { f(current->value); current = current->next; } }
内容的提问来源于stack exchange,提问作者Michaelangelo Meucci
相关产品推荐
相关产品推荐

