C语言学习笔记20260711-C语言动态内存管理四大核心函数详解及用例 📅 2026/7/12 4:27:34 C语言学习笔记20260711-C语言动态内存管理四大核心函数详解及用例核心头文件stdlib.h适用场景运行时才知道大小、需要跨函数共享数据、处理大型数据结构一、四大核心函数详解1. malloc - 分配未初始化内存函数原型void*malloc(size_tsize);功能说明在堆区分配一块大小为size字节的连续内存内存内容未初始化垃圾值。代码示例#includestdio.h#includestdlib.hintmain(void){// 分配10个int的空间int*arr(int*)malloc(10*sizeof(int));// ⚠️ 必须检查返回值if(arrNULL){perror(malloc failed);return1;}// 未初始化的内存是垃圾值必须手动赋值for(inti0;i10;i){arr[i]i*2;}printf(arr %d\n,arr);// 输出: arr 10free(arr);arrNULL;// 防止悬空指针return0;}关键要点返回void*C语言中可隐式转换但建议显式强转提高可读性分配失败返回NULL必须检查内存内容是未定义的垃圾值2. calloc - 分配并清零内存函数原型void*calloc(size_tnum,size_tsize);功能说明分配num个大小为size字节的内存块并自动将所有字节初始化为0。代码示例#includestdio.h#includestdlib.hintmain(void){// 分配10个int且全部初始化为0int*arr(int*)calloc(10,sizeof(int));if(arrNULL){perror(calloc failed);return1;}// 验证是否全为0for(inti0;i10;i){printf(%d ,arr[i]);// 输出: 0 0 0 0 0 0 0 0 0 0}printf(\n);free(arr);arrNULL;return0;}关键要点等价于malloc(num * size)memset(ptr, 0, num * size)适合数组、结构体、哈希表等需要清零的场景性能略低于malloc多了清零操作3. realloc - 调整内存大小函数原型void*realloc(void*ptr,size_tsize);功能说明调整已分配内存块的大小行为逻辑如下参数情况等价操作说明ptr NULLmalloc(size)等同于分配新内存size 0且ptr ! NULLfree(ptr)等同于释放内存原空间足够原地扩展返回原指针原空间不足重新分配拷贝释放旧块返回新指针⚠️ 致命陷阱如果realloc失败返回NULL原内存块依然有效且未释放。直接用原指针接收返回值会导致内存泄漏。代码示例#includestdio.h#includestdlib.hintmain(void){int*arr(int*)malloc(5*sizeof(int));if(arrNULL)return1;for(inti0;i5;i)arr[i]i;// ✅ 正确用法用临时指针接收返回值int*tmp(int*)realloc(arr,10*sizeof(int));if(tmpNULL){perror(realloc failed);free(arr);// realloc失败时原内存仍有效必须手动释放return1;}arrtmp;// 确认成功后再赋值给原指针// 新增的空间内容是未初始化的for(inti5;i10;i)arr[i]i*10;for(inti0;i10;i)printf(%d ,arr[i]);printf(\n);free(arr);arrNULL;return0;}关键要点永远用临时指针接收返回值扩容后新增的内存未初始化缩容时数据会被截断但不会丢失已保留部分4. free - 释放内存函数原型voidfree(void*ptr);功能说明释放之前由malloc/calloc/realloc分配的堆内存。代码示例#includestdio.h#includestdlib.hintmain(void){int*p(int*)malloc(sizeof(int));if(pNULL)return1;*p42;printf(*p %d\n,*p);free(p);pNULL;// ✅ 释放后立即置NULL防止悬空指针// free(p); // ❌ 如果p没置NULL这里会崩溃重复释放free(p);// ✅ free(NULL) 是安全的什么都不做return0;}关键要点只能释放堆内存不能释放栈变量、全局变量、字符串常量free(NULL)是安全操作什么都不做重复释放是未定义行为会导致堆损坏释放后指针变成悬空指针必须置NULL二、常见错误与最佳实践错误速查表错误类型说明后果预防措施未检查返回值malloc失败返回NULL直接使用段错误每次分配后必须检查 NULL内存泄漏分配后未free或指针丢失内存耗尽遵循谁分配谁释放原则悬空指针free后未置NULL再次使用未定义行为free(p); p NULL;形成肌肉记忆重复释放同一指针free两次堆损坏/崩溃置NULL后free是安全的越界访问读写超出分配大小的内存未定义行为始终记录分配大小使用工具检测realloc 错误用法ptr realloc(ptr, size)失败时内存泄漏用临时指针接收成功后再赋值释放栈内存free(local_var)崩溃只释放堆内存释放后使用访问已free的内存未定义行为释放后立即置NULL最佳实践清单分配必检查每次malloc/calloc/realloc后检查返回值释放必置空free(p); p NULL;写成固定套路realloc用临时指针避免失败时内存泄漏记录分配大小用变量或结构体成员保存大小避免越界谁分配谁释放明确内存所有权避免重复释放或泄漏函数返回前检查确保所有路径都正确释放内存使用工具检测不要依赖肉眼排查内存问题三、实战综合示例动态数组封装#includestdio.h#includestdlib.h#includestring.htypedefstruct{int*data;size_tsize;size_tcapacity;}DynArray;// 初始化intdynarray_init(DynArray*arr,size_tinit_cap){arr-data(int*)calloc(init_cap,sizeof(int));if(arr-dataNULL)return-1;arr-size0;arr-capacityinit_cap;return0;}// 追加元素intdynarray_push(DynArray*arr,intvalue){if(arr-sizearr-capacity){size_tnew_caparr-capacity*2;int*tmp(int*)realloc(arr-data,new_cap*sizeof(int));if(tmpNULL)return-1;arr-datatmp;arr-capacitynew_cap;}arr-data[arr-size]value;return0;}// 释放voiddynarray_free(DynArray*arr){free(arr-data);arr-dataNULL;arr-size0;arr-capacity0;}intmain(void){DynArray arr;if(dynarray_init(arr,4)!0){perror(init failed);return1;}for(inti0;i10;i){if(dynarray_push(arr,i*i)!0){perror(push failed);dynarray_free(arr);return1;}}for(size_ti0;iarr.size;i){printf(%d ,arr.data[i]);}printf(\n);dynarray_free(arr);return0;}四、总结口诀分配必检查释放必置空。Realloc 用临时越界要命凶。谁分配谁释放工具勤检测。堆上无小事谨慎保平安。最后更新2026-07-11