链表结构体中数组内存释放问题:double free错误排查
链表内存释放崩溃问题分析与解决
问题现象
- 不调用
dumpGroupingOrder()函数时,程序可正常运行,但存在内存泄漏,Valgrind检测结果:
LEAK SUMMARY:
definitely lost: 48 bytes in 1 blocks
indirectly lost: 328 bytes in 11 blocks
- 调用该函数后,无内存泄漏,但程序崩溃,报错
double free or corruption (out),Valgrind给出的错误信息:
Invalid free() / delete / delete[] / realloc()
at 0x48399AB: free (vg_replace_malloc.c:538)
by 0x10929A: dumpGroupingOrder (code-stack.c:54)
by 0x10952F: main (code-stack.c:88)
Address 0x1fff0000b0 is on thread 1's stack
in frame #2, created by main (code-stack.c:74)
完整可复现代码
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <math.h> struct groupingOrder { int *taxa; size_t groupeSize; long double distance; struct groupingOrder* next; }; void addGroup( struct groupingOrder* HEAD, int * groupe, size_t groupeSize, long double distance ) { struct groupingOrder* temp = HEAD; // Moving the last unit structure while( temp->next != NULL ) { temp = temp->next; } // Allocating space for the unit structure struct groupingOrder* nouveau = malloc( sizeof( struct groupingOrder ) ); nouveau->distance = distance; nouveau->groupeSize = groupeSize; // Allocating variable space for the list of taxa to be stored nouveau->taxa = malloc( groupeSize * sizeof( int ) ); for( int i = 0; i < groupeSize; i++) { nouveau->taxa[i] = groupe[i]; } // Linking the new unit structure to the previous one temp->next = nouveau; // Specifying the NULL pointer to label the last unit nouveau->next = NULL; } void dumpGroupingOrder( struct groupingOrder* HEAD ) { struct groupingOrder* temp = HEAD; while( temp != NULL ) { // Copying the address of the current unit struct groupingOrder* dumpTarget = temp; // Storing the address of the next unit before dumping the // current one. temp = temp->next; // Dumping the current unit and all its allocated variables if( dumpTarget->taxa != NULL ) { free( dumpTarget->taxa ); printf( " #" ); } free( dumpTarget ); printf( "* " ); } } void showGroupingOrder( struct groupingOrder* HEAD ) { struct groupingOrder* temp = HEAD; while( temp->next != NULL ) { temp = temp->next; for( int i = 0; i < temp->groupeSize; i++) { printf( " %d", temp->taxa[i] ); } printf( " %Lf\n", temp->distance ); } } int main() { size_t groupeSize[6] = { 2,2,4,2,5,7 }; int groupes[][7] = { {2,3}, {5,1}, {2,3,5,1}, {7,4}, {2,3,5,1,6}, {2,3,5,1,6,7,4} }; struct groupingOrder HEAD = { .next = NULL }; for( int i = 0; i < 6; i++ ){ addGroup( &HEAD, groupes[i], groupeSize[i], 0.0003 ); } showGroupingOrder( &HEAD ); printf( "\n\n" ); dumpGroupingOrder( &HEAD ); return 0; }
问题根源
main函数中的HEAD节点是栈上分配的变量(struct groupingOrder HEAD = { .next = NULL };),而dumpGroupingOrder()函数从HEAD开始遍历,最终会执行free(dumpTarget)来释放HEAD本身——栈内存不属于堆内存范畴,不能用free()释放,这直接触发了内存错误。
修复方案
修改dumpGroupingOrder()函数,从HEAD->next开始遍历释放,只释放链表中通过malloc分配的节点,跳过栈上的HEAD节点:
void dumpGroupingOrder( struct groupingOrder* HEAD ) { // 从第一个堆分配的节点开始,跳过栈上的HEAD struct groupingOrder* temp = HEAD->next; while( temp != NULL ) { struct groupingOrder* dumpTarget = temp; temp = temp->next; if( dumpTarget->taxa != NULL ) { free( dumpTarget->taxa ); printf( " #" ); } free( dumpTarget ); printf( "* " ); } // 可选:将HEAD的next置为NULL,避免野指针 HEAD->next = NULL; }
验证结果
修改后程序运行无崩溃,Valgrind检测无内存泄漏,也不再出现double free错误。
内容的提问来源于stack exchange,提问作者Goëtay
相关产品推荐
相关产品推荐

