1.为啥存在动态内存开辟?
在c/c++语言中,编写程序有时不能确定数组应该定义为多大,因此这时在程序运行时要根据需要从系统中动态多地获得内存空间。所谓动态内存分配,就是指在程序执行的过程中动态地分配或者回收存储空间的分配内存的方法。动态内存分配不像数组等静态内存分配方法那样需要预先分配存储空间,而是由系统根据程序的需要即时分配,且分配的大小就是程序要求的大小。
动态内存管理:malloc,calloc,realloc,free。都存放在堆区。头文件都可以是<stdlib.h>
2.malloc函数:
malloc函数(全称memory allocation函数),是C语言中进行内存动态分配的标准库函数[1],中文叫动态内存分配,用于申请一块连续的指定大小的内存块区域以void*类型返回分配的内存区域地址。
使用malloc函数,如果分配成功则返回指向被分配内存的指针(此存储区中的初始值不确定),否则返回空指针NULL。头文件是stdlib.h/malloc.h。
它的作用是开辟空间,并返回开辟空间的起始地址:void*(size_t m)--我一次开辟多少字节地址。
看下面的代码
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
int main()
{
int* p = (int*)malloc(40);
if (p == NULL)//没开辟成功
{
perror("malloc");
return 1;//C语言中好像默认如果结果错误返回1,正确返回0。
}
else if (p != NULL)
{
for (int i = 0; i < 10; i++)
{
*p = i;
p++;
}
}
return 0;
}
上面又加上else if是更好理解。这就是malloc具体应用。
有人不理解p++啥意思,没有p++是在一个地址上一直改变,因为地址未变。
但是当我们开辟完空间后使用后还要释放了。要不这么多内存就浪费了。
注意free函数只能释放动态开辟的空间
free能释放空间,但我们想一想,刚才刚才代码中p已经变成10,那他在开辟空间的最后位置,那我再释放它毫无意义,但是我们现在又找不到起始地址咋办?很简单,刚开始就保存不用就可以了。
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
int main()
{
int* ptr = (int*)malloc(40);//保存首元素地址为了更好释放
int* p = ptr;
if (p == NULL)//没开辟成功
{
perror("malloc");
return 1;//C语言中好像默认如果结果错误返回1,正确返回0。
}
else if (p != NULL)
{
for (int i = 0; i < 10; i++)
{
*p = i;
p++;
}
}
free(ptr);
return 0;
}
这样就行了,但是我们把ptr空间释放后,ptr就是野指针了,为了避免被错误使用,我们直接领他为空指针即可——ptr=NULL;
如果我们不释放动态申请的内存时:
1.程序结束:申请的内存由操作系统自动回收。
2.程序未结束:动态内存不会自动回收,就会形成内存泄漏。
3.calloc:
函数原型:void* calloc(unsigned int num,unsigned int size);
功能:在内存的动态存储区中分配num个长度为size的连续空间,函数返回一个指向分配起始地址的指针;如果分配不成功,返回NULL。
我可以这样写calloc(10,sizeof(int))我开辟了10个int型整形空间。
calloc相对于malloc最大区别是calloc内存初始化为0,接下来我们来实现它。
这就是我开辟10整形空间,且全部初始化为0.
4.realloc:调整内存
realloc函数为C语言函数。其功能为先判断当前的指针是否有足够的连续空间,如果有,扩大mem_address指向的地址,并且将mem_address返回,如果空间不够,先按照newsize指定的大小分配空间,将原有数据从头到尾拷贝到新分配的内存区域,而后释放原来mem_address所指内存区域,同时返回新分配的内存区域的首地址,即重新分配存储器块的地址。
函数原型:void *realloc(void *mem_address, unsigned int newsize);
realloc函数是为调整内存设置的,比如我们用malloc设置的内存有点小,我想在开辟10个整形,咋办,首先要找的要开辟空间的起始地址,我们也要写扩大多少这就是他函数原型。
它的返回值也很有意思,如果malloc函数在一块空间开辟后还有空间能容纳realloc所需要的空间,那他返回malloc的其实地址,否则他又找一块空间重新开辟,它先把原来的数据拷贝到新空间,把原空间释放掉,并且返回新空间的地址,开辟不成功则返回NULL。
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
int main()
{
int* p = (int*)malloc(40);
if (p == NULL)
{
perror(malloc);
return 1;
}
for (int i = 0; i < 10; i++)
{
*p = i;
p++;
}
int* ptr = (int*)realloc(p, 80);
if (ptr != NULL);//扩容成功
{
p = ptr;//把地址传给p即可
}
free(p);
p = NULL;
return 0;
}
常见的动态内存管理的·错误及办法:
1.对malloc函数返回值进行探空操作,如果是NULL,而你还在使用,那不浪费时间吗。
2.对动态开辟空间的越界访问——内存边界主动检查。
3.对非动态开辟的空间用free函数释放。
4.使用free函数释放动态开辟的一部分空间。——保存起始地址
5.对同一块内存空间多次释放——释放一次