使用free()删除链表元素数据时触发free(): invalid pointer错误求助
我正在完成一项编程练习,要求实现函数ft_list_remove_if:移除链表中所有经cmp函数与data_ref对比返回0的元素,且需调用free_fct释放待删除元素的数据。函数原型为:
void ft_list_remove_if(t_list **begin_list, void *data_ref, int (*cmp)(), void (*free_fct)(void *));
我在调用free()释放数据元素时,始终触发free() invalid pointer Aborted (core dumped)错误。以下是我的相关代码:
typedef struct llist { void *a; struct llist *next; } t_list; t_list *ft_create_elem(void *data) { t_list *ptr; ptr = NULL; ptr = malloc(sizeof(t_list)); ptr->a = data; ptr->next = NULL; /*NOTE 我另有一个函数负责将新创建的元素链接到前一个元素*/ return (ptr); } int ft_strcmp(char *s1, char *s2) { int i; i = 0; while (s1[i] == s2[i] && s1[i] != '\0' && s2[i] != '\0') { i++; } return (s1[i] - s2[i]); } void *ft_list_remove_if(t_list **begin_list, void *data_ref, int (*cmp)(), void (*free_fct)(void *)) { while(*begin_list) { if (cmp(((*begin_list)->a), data_ref) == 0) free_fct((*begin_list)->a); *begin_list = (*begin_list)->next; } }
我通过ft_list_remove_if(&toprint, "Third", ft_strcmp, free);调用该函数,toprint是包含5个元素(数据分别为"First"、"Second"、"Third"、"Fourth"、"Fifth")的链表头指针,目标是删除"Third"元素。
我可以修改(*begin_list)->a的内容,但无法释放其中的数据,想请教问题所在:(*begin_list)->a是不是不能传给free()的有效指针?是不是因为ft_create_elem中直接将data赋值给void *a?尝试赋值a = &data时输出乱码,该如何解决?
问题原因与解决方法
核心问题:你在释放字符串字面量的指针
你创建链表时传入的"First""Third"这类是字符串字面量,它们存储在程序的只读数据段,不是通过malloc/calloc/realloc分配的堆内存。free()只能释放堆上的内存,对只读段指针调用free()必然触发无效指针错误。直接赋值data的问题
ft_create_elem里ptr->a = data只是把传入的指针值直接存起来。如果传入的是字符串字面量,ptr->a指向的就是只读内存,不能被free();只有当传入的是堆上分配的字符串(比如malloc出来的),free()才合法。a = &data乱码的原因data是ft_create_elem的函数参数,属于栈上的局部变量。函数执行完后栈帧被销毁,&data指向的内存会被回收复用,后续访问就会出现乱码,这是典型的悬垂指针问题。具体解决步骤
创建元素时深拷贝字符串到堆内存:修改
ft_create_elem,对传入的字符串做深拷贝,用strdup或者自己实现malloc+strcpy:t_list *ft_create_elem(void *data) { t_list *ptr = malloc(sizeof(t_list)); if (!ptr) return NULL; // 假设data是字符串,做深拷贝 ptr->a = strdup((char*)data); ptr->next = NULL; return ptr; }这样
ptr->a指向堆内存,调用free()就合法了。修复
ft_list_remove_if的逻辑漏洞:你现在的函数只释放了数据,没移除链表节点,还会丢失整个链表指针。正确逻辑如下:void ft_list_remove_if(t_list **begin_list, void *data_ref, int (*cmp)(), void (*free_fct)(void *)) { t_list *curr; t_list *tmp; // 先处理链表头部需要删除的节点 while (*begin_list && cmp((*begin_list)->a, data_ref) == 0) { curr = *begin_list; *begin_list = curr->next; free_fct(curr->a); free(curr); // 还要释放链表节点本身 } // 处理中间和尾部的节点 curr = *begin_list; while (curr && curr->next) { if (cmp(curr->next->a, data_ref) == 0) { tmp = curr->next; curr->next = tmp->next; free_fct(tmp->a); free(tmp); } else { curr = curr->next; } } }这个版本会正确移除节点,同时释放数据和节点内存,避免内存泄漏和逻辑错误。
内容的提问来源于stack exchange,提问作者shamalamadingdong

