C语言union类型双向链表节点值全重复问题求解
问题原因
你的链表所有节点值重复是多个逻辑错误共同导致的:
- 核心错误:节点存储值时直接复用了同一块内存地址。你定义的
values是全局唯一的union数组,三次调用插入函数时传的都是这个数组的首地址,而插入函数里没有做值拷贝,直接把外部传入的地址存在节点里。后续每次输入新值都是在覆盖这块公共内存的内容,最终所有节点读到的都是最后一次写入的值。 - 笔误:插入函数定义的名字是
push_none,主函数里调用的名字是push_node,代码无法正常编译。 - 存储逻辑和union特性不匹配:union同一时间只能保存一个成员的值,你每轮循环连续给union的不同成员赋值3次,前面写的值会被后面的覆盖;且你插入节点时只传了数组首地址,根本拿不到同轮输入的另外2个值。
- 遍历错误:输出时直接移动链表的
head指针,遍历结束后原链表的头节点地址丢失,会造成内存泄漏。 - 多余的指针开销:节点里定义的是union指针,多了一层不必要的内存寻址,还容易出现野指针问题。
修复方案
- 调整节点结构体,直接存储union值(不需要指针),如果需要一个节点存多个不同类型的值,额外给每个值加类型标记,避免读值时类型不匹配。如果需求是每个节点只存1个值,直接把插入逻辑移到值读取的循环内,每读入一个值就插入一次即可。
- 统一插入函数名,插入节点时做值拷贝,不要直接存储外部传入的指针地址,保证每个节点的值存在独立的内存空间。
- 遍历链表时用临时游标指针移动,不要修改链表原有的head、tail指针。
- 程序结束前手动释放所有申请的堆内存(包括节点、strdup生成的字符串、链表本体),避免内存泄漏。
修正后可运行代码(按每个节点存3个带类型标记的值的逻辑实现)
#include <stdio.h> #include <stdlib.h> #include <string.h> // 带类型标记的单个值结构 typedef struct { int type; // 1:int 2:double 3:string union { int as_int; double as_double; char *as_str; } val; } typed_val; struct list_t{ struct node_t *head; struct node_t *tail; }; struct node_t { typed_val vals[3]; // 每个节点存3个带类型的值,直接存值不存指针 struct node_t *prev; struct node_t *next; }; // 统一函数名 int push_node(struct list_t *list, typed_val* value); int push_node(struct list_t *list, typed_val* value){ if(list==NULL || value==NULL)return 1; struct node_t *node = (struct node_t*) malloc(sizeof(struct node_t)); if(node==NULL)return 2; // 做值拷贝,每个节点存独立的副本,不依赖外部内存 memcpy(node->vals, value, sizeof(typed_val)*3); if(list->head==NULL){ node->next=NULL; node->prev=NULL; list->head = node; list->tail=node; } else{ node->next=NULL; node->prev = list->tail; list->tail->next = node; list->tail = node; } return 0; } int main() { struct list_t * list = malloc(sizeof(struct list_t)); list->tail=NULL; list->head=NULL; printf("Create a double linked list of 3 element :"); char buf[100],**ptr=NULL; typed_val values[3]; // 每轮临时存3个值,插入时做拷贝 int i=0; // 数据输入 while (i<3){ int j=0; while(j<3){ while(1){ printf("\n1 - INT\n2 -DOUBLE\n3 - CHAR\nEnter type:"); scanf("%d", &values[j].type); if(values[j].type>0 && values[j].type<4)break; } printf("\nEnter value :"); scanf("%s",buf); if(values[j].type==1){ values[j].val.as_int = (int)strtol(buf,ptr,10); } else if(values[j].type==2){ values[j].val.as_double = strtod(buf,ptr); } else if(values[j].type==3){ values[j].val.as_str = strdup(buf); } j++; } // 插入时传入临时数组地址,函数内会做拷贝,后续覆盖values不影响已插入节点 push_node(list,values); i++; } i=0; // 用临时指针遍历,不修改原head struct node_t *cur = list->head; while(cur!=NULL){ int j=0; while(j<3){ if(cur->vals[j].type==1){ printf("%d ",cur->vals[j].val.as_int); } if(cur->vals[j].type==2){ printf("%lf ",cur->vals[j].val.as_double); } if(cur->vals[j].type==3){ printf("%s ",cur->vals[j].val.as_str); } j++; } printf("\n"); i++; cur = cur->next; } // 内存释放逻辑可自行补充:遍历所有节点,先释放节点内strdup生成的字符串,再释放节点本身,最后释放list本体 return 0; }
如果你实际需求是每个节点只存1个值,只需要把节点里的
typed_val vals[3]改成单个typed_val val,把push_node调用移到j循环内部,每读一个值就插入一次即可,逻辑更简单。
内容的提问来源于stack exchange,提问作者Joki_004
相关产品推荐
相关产品推荐

