C语言学习笔记20260711-C语言动态内存管理四大核心函数详解及用例
2026/7/12 4:27:26 网站建设 项目流程

C语言学习笔记20260711-C语言动态内存管理四大核心函数详解及用例

核心头文件:<stdlib.h>
适用场景:运行时才知道大小、需要跨函数共享数据、处理大型数据结构


一、四大核心函数详解

1. malloc - 分配未初始化内存

函数原型
void*malloc(size_tsize);
功能说明

在堆区分配一块大小为size字节的连续内存,内存内容未初始化(垃圾值)。

代码示例
#include<stdio.h>#include<stdlib.h>intmain(void){// 分配10个int的空间int*arr=(int*)malloc(10*sizeof(int));// ⚠️ 必须检查返回值if(arr==NULL){perror("malloc failed");return1;}// 未初始化的内存是垃圾值,必须手动赋值for(inti=0;i<10;i++){arr[i]=i*2;}printf("arr = %d\n",arr);// 输出: arr = 10free(arr);arr=NULL;// 防止悬空指针return0;}
关键要点
  • 返回void*,C语言中可隐式转换,但建议显式强转提高可读性
  • 分配失败返回NULL必须检查
  • 内存内容是未定义的垃圾值

2. calloc - 分配并清零内存

函数原型
void*calloc(size_tnum,size_tsize);
功能说明

分配num个大小为size字节的内存块,并自动将所有字节初始化为0

代码示例
#include<stdio.h>#include<stdlib.h>intmain(void){// 分配10个int,且全部初始化为0int*arr=(int*)calloc(10,sizeof(int));if(arr==NULL){perror("calloc failed");return1;}// 验证是否全为0for(inti=0;i<10;i++){printf("%d ",arr[i]);// 输出: 0 0 0 0 0 0 0 0 0 0}printf("\n");free(arr);arr=NULL;return0;}
关键要点
  • 等价于malloc(num * size)+memset(ptr, 0, num * size)
  • 适合数组、结构体、哈希表等需要清零的场景
  • 性能略低于malloc(多了清零操作)

3. realloc - 调整内存大小

函数原型
void*realloc(void*ptr,size_tsize);
功能说明

调整已分配内存块的大小,行为逻辑如下:

参数情况等价操作说明
ptr == NULLmalloc(size)等同于分配新内存
size == 0ptr != NULLfree(ptr)等同于释放内存
原空间足够原地扩展返回原指针
原空间不足重新分配+拷贝+释放旧块返回新指针
⚠️ 致命陷阱

如果realloc失败返回NULL原内存块依然有效且未释放。直接用原指针接收返回值会导致内存泄漏。

代码示例
#include<stdio.h>#include<stdlib.h>intmain(void){int*arr=(int*)malloc(5*sizeof(int));if(arr==NULL)return1;for(inti=0;i<5;i++)arr[i]=i;// ✅ 正确用法:用临时指针接收返回值int*tmp=(int*)realloc(arr,10*sizeof(int));if(tmp==NULL){perror("realloc failed");free(arr);// realloc失败时,原内存仍有效,必须手动释放return1;}arr=tmp;// 确认成功后再赋值给原指针// 新增的空间内容是未初始化的for(inti=5;i<10;i++)arr[i]=i*10;for(inti=0;i<10;i++)printf("%d ",arr[i]);printf("\n");free(arr);arr=NULL;return0;}
关键要点
  • 永远用临时指针接收返回值
  • 扩容后新增的内存未初始化
  • 缩容时数据会被截断,但不会丢失已保留部分

4. free - 释放内存

函数原型
voidfree(void*ptr);
功能说明

释放之前由malloc/calloc/realloc分配的堆内存。

代码示例
#include<stdio.h>#include<stdlib.h>intmain(void){int*p=(int*)malloc(sizeof(int));if(p==NULL)return1;*p=42;printf("*p = %d\n",*p);free(p);p=NULL;// ✅ 释放后立即置NULL,防止悬空指针// free(p); // ❌ 如果p没置NULL,这里会崩溃(重复释放)free(p);// ✅ free(NULL) 是安全的,什么都不做return0;}
关键要点
  • 只能释放堆内存,不能释放栈变量、全局变量、字符串常量
  • free(NULL)安全操作,什么都不做
  • 重复释放是未定义行为,会导致堆损坏
  • 释放后指针变成悬空指针,必须置NULL

二、常见错误与最佳实践

错误速查表

错误类型说明后果预防措施
未检查返回值malloc失败返回NULL,直接使用段错误每次分配后必须检查== NULL
内存泄漏分配后未free,或指针丢失内存耗尽遵循"谁分配谁释放"原则
悬空指针free后未置NULL,再次使用未定义行为free(p); p = NULL;形成肌肉记忆
重复释放同一指针free两次堆损坏/崩溃NULLfree是安全的
越界访问读写超出分配大小的内存未定义行为始终记录分配大小,使用工具检测
realloc 错误用法ptr = realloc(ptr, size)失败时内存泄漏用临时指针接收,成功后再赋值
释放栈内存free(&local_var)崩溃只释放堆内存
释放后使用访问已free的内存未定义行为释放后立即置NULL

最佳实践清单

  1. 分配必检查:每次malloc/calloc/realloc后检查返回值
  2. 释放必置空free(p); p = NULL;写成固定套路
  3. realloc用临时指针:避免失败时内存泄漏
  4. 记录分配大小:用变量或结构体成员保存大小,避免越界
  5. 谁分配谁释放:明确内存所有权,避免重复释放或泄漏
  6. 函数返回前检查:确保所有路径都正确释放内存
  7. 使用工具检测:不要依赖肉眼排查内存问题

三、实战综合示例

动态数组封装

#include<stdio.h>#include<stdlib.h>#include<string.h>typedefstruct{int*data;size_tsize;size_tcapacity;}DynArray;// 初始化intdynarray_init(DynArray*arr,size_tinit_cap){arr->data=(int*)calloc(init_cap,sizeof(int));if(arr->data==NULL)return-1;arr->size=0;arr->capacity=init_cap;return0;}// 追加元素intdynarray_push(DynArray*arr,intvalue){if(arr->size>=arr->capacity){size_tnew_cap=arr->capacity*2;int*tmp=(int*)realloc(arr->data,new_cap*sizeof(int));if(tmp==NULL)return-1;arr->data=tmp;arr->capacity=new_cap;}arr->data[arr->size++]=value;return0;}// 释放voiddynarray_free(DynArray*arr){free(arr->data);arr->data=NULL;arr->size=0;arr->capacity=0;}intmain(void){DynArray arr;if(dynarray_init(&arr,4)!=0){perror("init failed");return1;}for(inti=0;i<10;i++){if(dynarray_push(&arr,i*i)!=0){perror("push failed");dynarray_free(&arr);return1;}}for(size_ti=0;i<arr.size;i++){printf("%d ",arr.data[i]);}printf("\n");dynarray_free(&arr);return0;}

四、总结口诀

分配必检查,释放必置空。
Realloc 用临时,越界要命凶。
谁分配谁释放,工具勤检测。
堆上无小事,谨慎保平安。


最后更新:2026-07-11

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询