C语言调用free释放malloc分配的指针时触发无效指针错误排查
错误根因
你的代码存在3处逻辑错误,直接触发无效指针报错和内存泄漏:
- 字符串赋值逻辑完全错误:你先为
data[0]、data[1]调用malloc分配了堆内存,紧接着直接用=把字符串常量的地址赋值给这两个指针。这一步不会把字符串内容写入你申请的堆空间,只会把指针从堆地址改写为指向只读数据段的字符串常量地址:一方面之前申请的堆内存再也无法访问,造成内存泄漏;另一方面你最终传入free的是指向只读常量的非堆指针,而free仅能释放malloc/calloc/realloc返回的堆内存地址,因此触发无效指针错误。 - 结构体内存分配大小错误:
list namesArray = (list)malloc(sizeof(list));中list是你typedef定义的结构体指针类型,sizeof(list)计算的是指针本身的长度(通常是4/8字节),不是struct List_Repr结构体的实际大小,会造成后续访问结构体成员时的内存越界。 - 字符串指针数组分配大小错误:
(*myList)->data = (string*)malloc(numOfNames*sizeof(string*));中data是string*类型,存储的元素是string(即char*),因此单元素大小应该取sizeof(string),多写一层解引用属于类型匹配错误,在多数指针长度一致的平台上不会立刻崩溃,但存在可移植性问题。
修正方案
- 字符串赋值禁止直接用
=将常量地址赋值给堆指针,引入string.h头文件后使用strcpy将字符串常量的内容拷贝到你申请的堆内存中,保证指针始终指向合法的堆地址。 - 所有
malloc调用的大小参数严格匹配目标类型:分配结构体时取结构体本身的长度,分配数组时取数组存储元素的类型长度。 - (可选)新手阶段不建议typedef隐藏指针类型,显式写结构体指针可以大幅降低类型层级判断错误的概率。
修正后可运行代码
#include <stdio.h> #include <stdlib.h> #include <string.h> typedef char* string; typedef struct List_Repr { string *data; } *list; void addNames(list *myList, int numOfNames){ // 修正数组元素大小计算 (*myList)->data = (string*)malloc(numOfNames * sizeof(string)); // 申请堆空间后用strcpy拷贝字符串内容 (*myList)->data[0] = (string)malloc(strlen("Raymond") + 1); strcpy((*myList)->data[0], "Raymond"); (*myList)->data[1] = (string)malloc(strlen("George") + 1); strcpy((*myList)->data[1], "George"); } void printNames(list *myList){ printf("%s\n", (*myList)->data[0]); printf("%s\n", (*myList)->data[1]); } int main() { // 修正结构体分配大小计算 list namesArray = (list)malloc(sizeof(struct List_Repr)); addNames(&namesArray, 2); printNames(&namesArray); // 所有待释放指针均为malloc返回的合法堆地址,无报错 free(namesArray->data[0]); free(namesArray->data[1]); free(namesArray->data); free(namesArray); return 0; }
注:你原先用
sizeof("Raymond")计算字符串长度是可行的,因为C语言中字符串常量末尾自动带\0结束符,sizeof会把结束符长度算入;换成strlen+1的写法更通用,后续修改字符串内容时不容易出现长度计算错误。
内容的提问来源于stack exchange,提问作者Raymond Arthur
相关产品推荐
相关产品推荐

