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

C语言单链表实现诡异内存问题:删节点时篡改其他节点价格

问题

实现包含{name, price}结构体元素的单链表时,以下两个函数出现异常行为:

  • PopFront(删除链表末尾元素)
  • PopByIndex(根据指定索引删除对应位置元素)

具体异常:函数虽能正确删除目标元素,但会莫名修改链表中另一个元素的price值。

示例:
删除前:

Name:        A  Price: 1.00
Name:        B  Price: 2.00
Name:        C  Price: 3.00
Name:        D  Price: 4.00
    --------
     |Menu|
    --------
(1) Add Element in Front
(2) Add Element in Back
(3) Add Element in Position
(4) Remove Element from Front
(5) Remove Element from Back
(6) Remove Element by Position
(7) Print List
(8) Calculate Itens Sum
(0) Exit Program

删除元素D后,元素A的price变为0.00:

Name:        A  Price: 0.00
Name:        B  Price: 2.00
Name:        C  Price: 3.00
    --------
     |Menu|
    --------
(1) Add Element in Front
(2) Add Element in Back
(3) Add Element in Position
(4) Remove Element from Front
(5) Remove Element from Back
(6) Remove Element by Position
(7) Print List
(8) Calculate Itens Sum
(0) Exit Program

通过gdb调试发现,异常发生在调用free()释放被删除元素内存的时刻。

最小可复现代码:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef struct Node Node;
typedef struct List List;

struct Node {
    char name[100];
    float price;
    int code;
    struct Node *prox; 
};

struct List{
    struct Node *head;
    int size;
};

Node* InstantiateNode() {
    Node *new_node = malloc(sizeof new_node);
    new_node->prox = NULL;
    return new_node;
}

void InitNode(Node *node, char *name, float price) {
    strcpy(node->name, name);
    node->price = price;
}

void PrintNode(Node node){
    printf("Name: %8s  Price: %.2f\n", node.name, node.price);
}

int EmptyList(List list) {
    return list.size == 0 ? 1 : 0;
}

List *InstantiateList(void) {
    List *new_list = malloc(sizeof new_list);
    new_list->head = NULL;
    new_list->size = 0;
    return new_list;
}

void InitList(List *list, Node *new_node) {
    new_node->prox = list->head;
    list->head = new_node;
    list->size++;
}

void PushFront(List *list, Node *new_node) {
    if (EmptyList(*list)) {
        InitList(list, new_node);
        return;
    }
    Node *tracker = list->head;
    while (tracker->prox != NULL)
        tracker = tracker->prox;
    tracker->prox = new_node;
    list->size++;
}

void PushBack(List *list, Node *new_node) {
        InitList(list, new_node);
}

Node *TrackListbyIndex(List lista, int index) {
    if (index > lista.size) {
        printf("Unreachable List index \n"); // Warning
        return NULL;
    }
    int cont = 0;
    Node *aux = lista.head;
    while (cont != index) {
        aux = aux->prox;
        cont++;
    }
    return aux;
}

void PushByIndex(List *list, Node *new_node, int index) {
    if (index == 0) {
        PushBack(list, new_node);
        return;
    }
    Node *aux = TrackListbyIndex(*list, index - 1);
    Node *aux2 = aux->prox;
    if (aux) {
        new_node->prox = aux->prox;
        aux->prox = new_node;
        list->size++;
    }
}

void PopFront(List *list) {
    if (EmptyList(*list))
        return;
    Node* aux = list->head;
    Node* tracker, *tracker_next;
    tracker = list->head;
    if (tracker->prox != NULL) {
        tracker_next = tracker->prox;
        while (tracker_next->prox != NULL) {
            tracker = tracker_next;
            tracker_next = tracker_next->prox;
        }    
        tracker->prox = NULL;
        aux = tracker_next;
    }
    if (aux == list->head) {
        list->head = NULL;
    }
    free(aux);
    list->size--;
}

void PopBack(List* list){
    if(EmptyList(*list))
        return;
    Node* aux = list->head;
    list->head = list->head->prox;
    free(aux);
    list->size--;   
}
void PopByIndex (List *list, int index) {
    if (index == 0) {
        PopBack(list);
        return;
    }
    //if (index + 1 == list->size) {
    //    PopFront(list);
    //    return;
    //}
    Node *aux, *aux_next;
    aux = TrackListbyIndex(*list, index - 1);
    if (aux) {
        aux_next = aux->prox;
        aux->prox = aux_next->prox;
        free(aux_next);
        list->size--;
    }
}

void PrintList(List list) {
    Node *tracker = list.head;
    while (tracker != NULL) {
        PrintNode(*tracker);
        tracker = tracker->prox;
    }
}

void Menu(void);
void HandleMenu(int);

int main(void) {
    int option;
    for (;;) {
        Menu();
        printf("Type your choosen:");
        scanf("%d", &option);
        system("clear");
        HandleMenu(option);
    }
    return 0;
}

void Menu(void) {
    printf("\t--------\n");
    printf("\t |Menu|\n");
    printf("\t--------\n");
    printf("(1) Add Element in Front\n");
    printf("(2) Add Element in Back\n");
    printf("(3) Add Element in Position\n");
    printf("(4) Remove Element from Front\n");
    printf("(5) Remove Element from Back\n");
    printf("(6) Remove Element by Position\n");
    printf("(7) Print List\n");
    printf("(8) Calculate Itens Sum\n");
    printf("(0) Exit Program\n");
}

void HandleMenu(int option) {
    static List *market_list = NULL;
    Node* new_node = NULL;
    char node_name[100];
    float node_price;
    int index;
    if (market_list == NULL)
       market_list = InstantiateList();
    switch (option) {
      case 0:
        exit(1);
        break;
      case 1:
        new_node = InstantiateNode();
        printf("Type the product's name: ");
        scanf(" %[^\n]", node_name);
        printf("Type the product's price: ");
        scanf("%f", &node_price);
        InitNode(new_node, node_name, node_price);
        PushFront(market_list, new_node);
        printf("Item Added\n");
        break;
      case 2:
        new_node = InstantiateNode();
        printf("Type the product's name: ");
        scanf(" %[^\n]", node_name);
        printf("Type the product's price: ");
        scanf("%f", &node_price);
        InitNode(new_node, node_name, node_price);
        PushBack(market_list, new_node);
        printf("Item Added\n");
        break;
      case 3:
        new_node = InstantiateNode();
        printf("Type the product's name: ");
        scanf(" %[^\n]", node_name);
        printf("Type the product's price: ");
        scanf("%f", &node_price);
        InitNode(new_node, node_name, node_price);
        printf("Type Index: ");
        scanf("%d", &index);
        PushByIndex(market_list, new_node, index-1);
        printf("Item Added\n");
        break;
      case 4:
        PopFront(market_list);
        break;
      case 5:
        PopBack(market_list);
        break;
      case 6:
        printf("Type Index: ");
        scanf("%d", &index);
        PopByIndex(market_list, index-1);
        break;
      case 7:
        PrintList(*market_list);
        break;
    }
}
问题排查与解决

核心原因

内存分配错误是问题的根源:

  • InstantiateNode()中,malloc(sizeof new_node)仅分配了指针大小的内存(32位系统4字节,64位系统8字节),但struct Node实际大小远超此值(仅name[100]就占100字节,加上其他成员总大小超过108字节)。分配的内存不足导致后续写入数据时越界,破坏了其他内存区域的内容,释放节点时内存管理器整理内存,触发了越界数据的暴露,表现为其他节点的price被篡改。
  • 同样的错误出现在InstantiateList()中,malloc(sizeof new_list)分配的是指针大小的内存,而非struct List的实际大小。

修复方案

修改两个内存分配函数,使用sizeof(struct Node)和sizeof(struct List)获取正确的内存大小,并增加分配失败检查:

  1. 修复InstantiateNode():
Node* InstantiateNode() {
    Node *new_node = malloc(sizeof(struct Node));
    if (new_node == NULL) {
        perror("malloc failed for Node");
        exit(EXIT_FAILURE);
    }
    new_node->prox = NULL;
    return new_node;
}
  1. 修复InstantiateList():
List *InstantiateList(void) {
    List *new_list = malloc(sizeof(struct List));
    if (new_list == NULL) {
        perror("malloc failed for List");
        exit(EXIT_FAILURE);
    }
    new_list->head = NULL;
    new_list->size = 0;
    return new_list;
}

额外优化建议

  • 所有内存分配操作后都应检查返回值,避免空指针错误。
  • 函数命名需修正:PushFront实际是在链表尾部添加元素,PushBack实际是在头部添加,建议重命名为PushBack(尾部添加)和PushFront(头部添加),保持语义一致。
  • TrackListbyIndex()中,index > lista.size的判断有误,应改为index >= lista.size,因为索引从0开始,最大有效索引为lista.size - 1。

内容的提问来源于Stack Exchange,提问作者GUSTAVO HENRIQUE NASCIMENTO DE

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.28 22:59:56