C语言链表程序求助:无法区分数字与字符,输出异常
C语言链表分离数字与字符问题修复
需求是编写C程序,将输入内容中的数字和字符分离后存入链表,再输出链表内容。但现有程序存在两个核心问题:
- 无论输入什么内容,输出结果仅显示
10 - 输入表达式
1+2+3时,无法正确分离其中的数字与运算符
原代码核心问题分析
- Union使用逻辑错误:
Union的成员共享同一块内存空间,同一时间只能存储一种类型的数据。原代码中既给operate赋值又给operand赋值,会导致数据覆盖;且赋值逻辑搞反了——数字应该存入operand,运算符存入operate,同时需要额外的标记位区分当前节点存储的是哪种类型。 - 节点创建逻辑错误:
addNode函数中仅创建了一个节点,然后在循环中反复覆盖这个节点的数据,最终链表中只加入了这一个节点,存储的是输入字符串的最后一个字符(输入1+2+3时,最后一个字符是换行符\n,ASCII码为10,所以输出operand为10)。 - 输入处理冲突:
main函数中先使用fgets读取表达式,之后用scanf读取用户选择,scanf会将输入缓冲区中的换行符留在其中,导致下一次调用fgets时直接读取空行。 - 链表遍历不完整:
displayNode函数中注释掉了遍历链表的循环,仅打印第一个节点的内容。 - 函数参数不匹配:
addNode和displayNode的定义没有参数,但main调用时传入了&top,属于参数类型不匹配;全局变量top的使用也不利于代码模块化。
修复后的完整代码
#include <stdio.h> #include <stdlib.h> #include <ctype.h> #include <string.h> // 枚举标记节点存储的类型 typedef enum { OPERAND, OPERATOR } NodeType; // 结构体存储数据和类型标记 typedef struct NodeData { NodeType type; union { int operand; char operate; } data; } NodeData; // 链表节点结构体 typedef struct Node { NodeData content; struct Node* next; } Node; // 创建新节点 Node* createNode(NodeType type, void* value) { Node* newNode = (Node*)malloc(sizeof(Node)); if (!newNode) { printf("内存分配失败\n"); exit(1); } newNode->content.type = type; if (type == OPERAND) { newNode->content.data.operand = *(int*)value; } else { newNode->content.data.operate = *(char*)value; } newNode->next = NULL; return newNode; } // 添加节点到链表尾部 void addNode(Node** head) { char expression[64]; printf("Enter expression: "); // 清理输入缓冲区的残留换行 int c; while ((c = getchar()) != '\n' && c != EOF); fgets(expression, 64, stdin); // 去掉fgets读取的换行符 expression[strcspn(expression, "\n")] = '\0'; for (int i = 0; i < strlen(expression); i++) { if (isdigit(expression[i])) { // 字符转整数 int num = expression[i] - '0'; Node* newNode = createNode(OPERAND, &num); if (*head == NULL) { *head = newNode; } else { Node* curr = *head; while (curr->next != NULL) { curr = curr->next; } curr->next = newNode; } } else if (expression[i] != ' ') { // 忽略空格 char op = expression[i]; Node* newNode = createNode(OPERATOR, &op); if (*head == NULL) { *head = newNode; } else { Node* curr = *head; while (curr->next != NULL) { curr = curr->next; } curr->next = newNode; } } } } // 打印链表内容 void displayNode(Node* head) { if (head == NULL) { printf("链表为空\n"); return; } Node* curr = head; printf("\n+---------------------------------+\n"); printf("运算符列表: "); while (curr != NULL) { if (curr->content.type == OPERATOR) { printf("%c ", curr->content.data.operate); } curr = curr->next; } printf("\n操作数列表: "); curr = head; while (curr != NULL) { if (curr->content.type == OPERAND) { printf("%d ", curr->content.data.operand); } curr = curr->next; } printf("\n+---------------------------------+\n"); } // 释放链表内存 void freeList(Node** head) { Node* curr = *head; while (curr != NULL) { Node* temp = curr; curr = curr->next; free(temp); } *head = NULL; } int main() { int menu = 1; Node* head = NULL; do { addNode(&head); displayNode(head); char cont; printf("\n是否继续输入表达式(Y/N)? "); scanf(" %c", &cont); if (cont == 'n' || cont == 'N') { menu = 0; } // 清理scanf后的换行符,避免影响下一次fgets while ((getchar()) != '\n'); } while (menu == 1); freeList(&head); return 0; }
关键修改说明
- 新增类型标记:用枚举
NodeType标记每个节点存储的是操作数还是运算符,配合结构体里的Union,避免数据覆盖问题。 - 循环创建节点:遍历输入字符串时,每遇到一个数字或运算符就创建一个新节点,确保每个元素都存入链表。
- 修复输入冲突:在
fgets前清理输入缓冲区的残留字符,scanf后也清理换行符,避免输入混乱。 - 完整遍历链表:修改
displayNode函数,分别遍历打印所有运算符和操作数。 - 内存管理:新增
freeList函数释放链表内存,避免内存泄漏;创建节点时增加内存分配失败判断。 - 字符转整数:将字符形式的数字转换为整数(
expression[i] - '0'),避免存储ASCII码值。
内容的提问来源于stack exchange,提问作者RobDaChad
相关产品推荐
相关产品推荐

