You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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;
}

关键修改说明

  1. 新增类型标记:用枚举NodeType标记每个节点存储的是操作数还是运算符,配合结构体里的Union,避免数据覆盖问题。
  2. 循环创建节点:遍历输入字符串时,每遇到一个数字或运算符就创建一个新节点,确保每个元素都存入链表。
  3. 修复输入冲突:在fgets前清理输入缓冲区的残留字符,scanf后也清理换行符,避免输入混乱。
  4. 完整遍历链表:修改displayNode函数,分别遍历打印所有运算符和操作数。
  5. 内存管理:新增freeList函数释放链表内存,避免内存泄漏;创建节点时增加内存分配失败判断。
  6. 字符转整数:将字符形式的数字转换为整数(expression[i] - '0'),避免存储ASCII码值。

内容的提问来源于stack exchange,提问作者RobDaChad

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.16 00:50:21