动态内存管理也叫动态内存开辟。指在程序运行时,根据需要动态地分配和释放内存空间的过程。它允许程序在运行时根据实际情况来动态地请求分配内存,以满足不同大小和数量的数据存储需求。
1) 内存分配:当程序需要更多的内存空间来存储数据时,它可以通过动态内存分配来请求一块合适大小的内存空间。malloc、calloc...
2) 内存释放:当不再需要某个内存空间时,程序可以通过动态内存释放将该空间归还给系统,以便其他部分可以使用。free
1 ) 堆分配:在堆中分配内存空间,可以使用诸如malloc、calloc、realloc
等函数进行分配,并使用free
函数进行释放。
2 ) 操作系统提供的动态内存管理:操作系统也提供了一些机制来管理进程的动态内存,例如Unix/Linux系统中的brk和sbrk系统调用,以及Windows系统中的HeapAlloc和HeapFree函数。
栈区申请空间(函数中的普通变量)
当函数被调用时,会在栈上分配一定大小的内存空间来存储函数内的局部变量和一些临时数据。这些变量的内存空间在函数执行完毕后会被自动释放,因此称为自动变量
#include
int main()
{
int a = 10;//4个字节
int arr[10] = {};//40个字节
return 0;
}
静态存储区
静态内存分配是通过定义全局变量、静态变量或者常量来实现的
int global_variable; // 全局变量,存储在静态存储区
static int static_variable; // 静态变量,存储在静态存储区
const int constant_value = 10; // 常量,存储在静态存储区
堆区申请空间(动态申请的内存)
malloc函数接收的参数:
malloc的特点:
malloc
会找到合适的空闲内存块,并且这样的内存是匿名的,换句话说说malloc
分配内存,但不会为其命名malloc
返回动态分配内存块的首字节地址,因此我们可以把该地址赋给一个指针变量,并用指针来访问内存#include
#include //errno
#include //strerro
#include //malloc
int main()
{
int* p = (int*)malloc(40); //malloc是(void*)类型,这里强制转换成(int*)
//进行检查:
if (p == NULL)
{
printf("%s\n", strerror(errno));
return 1; //异常返回
}
//使用内存
for (int i = 0; i < 10; i++)
{
*(p + i) = i;
}
//输出检查试试
for (int i = 0; i < 10; i++)
{
printf("%d ", *(p + i));
}
//程序退出系统会自动回收空间,回收用free(),防止内存泄漏
free(p); //先释放,free必须释放动态内存开辟的空间
p = NULL; //在赋值
return 0; //正常返回
}
为什么需要free( )
进行释放:
自动变量使用的内存数量在程序执行期间自动增加或减少,但是动态分配的内存数量只会增加,除非用
free ( )
进行释放 ;
关于内存泄露(memory leak):
假设我们的程序需要一直运行(循环)且我们忘记对该程序进行内存释放,那么我们的程序就不停的创建内存块,并且当函数(开辟内存)结束后,该内存块也无法再被使用和被访问,如此循环往复,我们有限的内存资源终将会被消耗殆尽;
实际上,也许在循环还没结束前就已经耗尽所有内存。这类问题被称为内存泄漏
calloc接受两个无符号整数作为参数:
calloc的特点:
#include
#include //errno
#include //strerro
#include //calloc
int main()
{
//calloc(size_t num, size_t size) 且数组所有值初始化为0
int* p = (int*)calloc(10, sizeof(int));
if (p == NULL)
{
printf("%s", strerror(errno));
return 1;
}
//使用内存
for (int i = 0; i < 10; i++)
{
*(p + i) = i;
}
//打印
for (int i = 0; i < 10; i++)
{
printf("%d ", *(p + i));
}
//释放
free(p);
p = NULL;
return 0;
}
realloc的出现意义:
realloc
函数可以做到对动态开辟内存的大小进行调整realloc追加的两种情况:
realloc
自动将之前占用的内存空间销毁过多使用realloc:
realloc也可等价为malloc:
realloc(NULL, 40);//传递空指针
malloc(40);
#include
#include //errno
#include //strerro
#include //realloc
int main()
{
//realloc 合理调整,使用内存
int* p = (int*)malloc(40);
if (p == NULL)
{
printf("%s", strerror(errno));
return 1;
}
//使用:1~10
for (int i = 0; i < 10; i++)
{
*(p + i) = i + 1;
}
//扩容,直接将ptr赋给p是危险的(扩容内存不足)
int* ptr = (int*)realloc(p, 80);
if (ptr != NULL)
{
p = ptr;
}
//输出查看数值,发现原来开辟赋值的数并没有改变
for (int i = o; i < 10; i++)
{
printf("%d", *(p + i));
}
free(p);
p = NULL;
return 0;
}
Debug Assertion Failed
没有判断指针是否为NULL就直接赋值
访问的内存大于开辟的内存造成了越界访问
int a = 10;
free(a);
改变了指针初始所指向的位置,想要释放该指针时无法释放掉全部所开辟的空间
多次用free释放同一个指针
第一次释放的时候原指针已经变成了野指针
(可以在第一次释放后给该指针赋值NULL)
内存泄漏
忘记free()
或者未执行free()
调用函数指针已经被释放
//错误代码
void GetMemory(char* p)
{
p = (char*)malloc(100); //函数调用结束 p 销毁
}
void Test(void)
{
char* str = NULL;
GetMemory(str);
strcpy(str, "xiangshuidajiao"); //此时 str = NULL 空指针的解引用
printf(str);
}
int main()
{
Test(); //无法正确打印
return 0;
}
过程分析:
调用Test ( ) -> 给指针 str 赋为 NULL -> 调用 GetMemory ( ) -> 给指针 p 开辟了 100 字节空间 但是调用结束p销毁,p的空间还给操作系统,但malloc的空间还存在且没有程序free其开辟空间(造成内存泄漏)-> 回到str str依然为空指针 -> 字符串函数解引用崩溃
正确修改的方法之一:
//正确代码
void GetMemory(char** p)
{
*p = (char*)malloc(100); //*p=str
}
void Test(void)
{
char* str = NULL;
GetMemory(&str); //str存放动态开辟的100字节的地址
strcpy(str, "xiangshuidajiao");
printf(str);
//释放
free(str);
str = NULL;
}
int main()
{
Test();
return 0;
}
[1]以上图片来源于:cplusplus官网
[2]参考视频:b站鹏哥
[3]参考书籍:C Primer Plush
[4]关于C语言内存五大区文章推荐:https://blog.csdn.net/m0_73381672/article/details/131726780
[5]关于字符串函数:https://blog.csdn.net/ggh_567/article/details/134609732