C语言中,我们常常都是在栈区中开辟空间的,就比如我下面的代码
int main()
{
int a = 0;
int arr[10];
}
以上的两行代码,分别向栈区申请了4个字节和40个字节的空间,可是我们有没有想过,申请这么多空间,万一我们用不上了怎么办,那岂不是浪费了吗?对的,借着这个问题,我们来聊一聊动态内存,所以“动态”,就是内存空间是可依据程序员的想法可作变动的空间
我们先来将两个函数
malloc和free
malloc————————————————————————————————
这是c语言提供的一个用于动态内存开辟的函数:
void * malloc(size_t size),size单位是字节
- 如果开辟成功,则返回一个指向该空间起始位置的地址
- 如果开辟失败,则返回空指针NULL
- 返回值是void *,所以malloc开辟的空间没有指定的什么类型,后面需要将其转换成什么类型,进行强制类型转换即可
- 如果size的值为0,那此时malloc的行为是未定义的,取决于编译器
接下来,举个例子
int main()
{
int * p = (int *)malloc(40);//malloc()里面存放40,意味着我要从堆区中开辟一个40字节长的空间,因为malloc返回的是一个void *的指针,我们要对这块内存空间存放整型,所以我们要给他转换成int *的指针,然后利用指针挨个访问
if(p==NULL)//malloc是有可能开辟失败的,所以要对其返回的指针进行判断,不为空指针才可以继续运行
{
perror("malloc");//打印开辟失败的原因
return 1;
}
for(int i = 0;i<10;i++)
{
*(p+i) = i+1;//对malloc开辟的空间,利用整型指针进行逐个赋值
}
return 0;
}


上面就是malloc函数的简单运用了,还是比较简单的,但是值得一提的是,动态内存是在堆区里开辟的空间,其与我们之前学过的局部变量,函数形参有所不同,后者程序会自动回收这块空间,而前者的空间回收通常有以下两种方法:1.使用free函数释放2.程序结束自动释放
那么接下来,我们来讲一讲free函数
free————————————————————————————————
括号里面填指向该被需要释放的动态内存的起始地址(也就是指针);
void free(void * ptr);
- 如果参数ptr指向的空间不是动态开辟的,则free函数的行为未定义
- 如果参数ptr是NULL指针,则函数什么是都不做
接下来,上代码
int main()
{
int * str = (int *)malloc(40);向动态内存开辟40个字节的空间
if(str==NULL)
{
perror("malloc");
return 1;
}
free(str);//回收str指向的动态内存空间
str = NULL;//这里的str需要置空,因为动态内存空间已经被回收了,但是str还是指向了那一块空间,相当于野指针,对其解引用就相当于非法访问内存了,所以要将其置空
return 0;
}
这里讲一下,为什么动态内存需要及时回收,我这里给一个极端的代码,从下方的代码可以看出,程序进入主函数后,调用了test函数,进而向堆区开辟了40个字节的空间,然后进行了while,但是whlie循环不退出,一直循环,所以上述给的两个释放动态内存的方法都没有用到(free函数和程序结束时自动回收),所以程序一直霸占着这块空间,也没人去去释放它,假设很多程序员都这么做呢?就会把整块堆区里面可以供给开辟动态内存的空间耗干,进行使得电脑运行起来卡顿,影响性能,所以我们在开辟动态内存时要及时地释放内存
void test()
{
int * p = (int *)malloc(40);
}
int main()
{
test();
while(1)
{
;
}
return 0;
}
calloc 和 realloc
calloc—————————————————————————
C语言还提供了一个函数叫calloc,calloc也可以用来动态内存分配。原型如下:
void * calloc (size_t num, size_t size);
- 函数的功能是为num个大小为size的元素开辟一块空间,并且把空间的每个字节都初始化为0。
- 与函数malloc的区别只在于calloc会在返回地址之前把申请的空间的每个字节初始化为0;
举个例子:
int main()
{
int * p = calloc(10,sizeof(int));
if(p==NULL)
{
return 1;
}
int i = 0;
for(i = 0;i<10;i++)
{
printf("%d ",*(p+i));
}
return 0;
本次运行的结果为:0 0 0 0 0 0 0 0 0 0,可见calloc把开辟的内存空间的每个字节都初始化了为0;
即

realloc————————————————————————
- realloc的出现让动态内存管理更加灵活
- 有时候我们会发现我们向堆区申请的动态内存太小了,那realloc函数就可以做到对动态开辟内存大小的调整。
void * realloc(void * ptr,size_t size)
ptr指向的是要被调整的动态内存的起始地址指针,size指的是重新被开辟的内存大小
我们通过几个图来了解一下realloc函数的使用逻辑(方法)。
扩展内存一般会遇到三种情况:
int main()
{
int * p = (int *)malloc(sizeof(int));//向堆区开辟了4个字节的空间
if(p==NULL)
{
return 1;
}
*p = 1;
int * ptr = (int *)realloc(p,sizeof(int) + 5*sizeof(int));//在四个字节空间的基础上,又开辟了20个字节
if(ptr==NULL)
{
return 1;
}
p = ptr;//将ptr赋给p
ptr = NULL;/及时将ptr置为空指针
free(p);//释放p指向的动态内存,也就是原先的加上后来增添的内存
p = NULL;
return 0;
}
(1.)向原内存后面进行扩展,也就是拼接上去

(2.)第二种情况,向后扩展的时候发现后面部分的内存已经有人使用了就比如下图

————那就需要向内存申请一块额外的空间

需要注意的是,原内存会被销毁,并且把原内存的数据拷贝到新内存上
(3.)第三种情况,也就是内存扩展开辟失败
这种情况下,系统会返回一个空指针(NULL),并且将原空间里面的数据全都还回去了
1037

被折叠的 条评论
为什么被折叠?



