引言:动态内存管理是学习C语言当中非常重要的一环,因为他涉及到计算机的空间储存管理,是一种新的开辟空间的方式
在此前我们开辟空间,都是在计算机的栈区,而用动态内存管理函数开辟的空间则在栈区,这让我们可以自由调节自己开辟的空间来使用
int a = 8;//在栈空间上开辟2个字节
char arr[10] = {0};//在栈空间上开辟10个字节的连续空间
遗憾的是,以上两种开辟空间的方式,有两大特点:
1.空间开辟的大小是固定的,在确定以后修改不了
2.数组在声明的时候,必须要指定数组长度,数组空间一旦确定下来就无法改变了。
这样的空间开辟方式让我们对于空间大小的灵活使用并不利好,所以我们需要一中可以灵活申请和释放空间的方式------动态内存。
1.malloc和free
学习过动态内存管理的大家都很熟悉这两个函数,一个用来申请开辟空间的大小,一个用来释放自己开辟的空间
void* malloc (size_t size);//用于开辟size个字节空间,返回值是void*,返回的是开辟的空间的首地址。
//因为在开辟函数之前malloc函数还不知道你需要用作什么样的数据类型,所以用void*来接收(在上一期有论述到噢),要用的时候自己定义
void free (void* ptr);//用于释放内存
tips:释放空间非常重要!!!
那么下面,我来给大家介绍一下他们是如何打配合的
#include <stido.h>
#include <stdlib.h>//使用动态内存函数,需要包含这个头文件
int main()
{
int* p = (int*)malloc(20);//20 个字节 - 存放5个整数,或者可以int*p= (int*)malloc(sizeof(int)*5);
if (p == NULL)
//因为开辟空间是会出现失败的情况的,而失败时,将会返回NULL,在申请内存后进行一个判断,有利于我们清楚自己写的程序有无出现状 况,是个好习惯噢!
{
perror("malloc");//这个函数可以反馈程序运行时出现的错误
return 1;
}
//使用空间
int i = 0;
for (i = 0; i < 5; i++)
{
*(p + i) = i + 1;
}
//释放内存
free(p);//传递给free函数的是要释放的内存空间的起始地址
p = NULL;//最后再把用完的空间设置为NULL,防止它成为野指针
return 0;
}
2.calloc和realloc
void* calloc (size_t num, size_t size);//calloc的使用方式和malloc几乎一模一样,知识 calloc多了一个功能:将你所开辟的空间都初始化为0
void* realloc (void* ptr, size_t size);//realloc就比较关键啦,因为它可以动态的帮我们任意更改开辟空间的大小
//ptr是要调整的内存地址,size是调整之后新大小,返回值为调整之后的内存起始位置。
realloc的使用使得我们对于空间管理可以更加的灵活,但是我们需要注意它开辟空间的两种情况
情况1:当拓展的内存后面有足够的空间可以开辟,要扩展内存就直接原有内存之后直接追加空间,原来空间的数据不发生变化。
情况2:原有空间之后没有足够多的空间时,扩展的方法是:在堆空间上另找⼀个合适大小的连续空间来使⽤。
这样函数返回的是⼀个新的内存地址。
下面画了一张图帮助大家理解
#include<stido.h>
#include<stdlib.h>
int main()
{
int* p = (int*)malloc(5 * sizeof(int));//开辟一个5个整型空间,20字节
if (p == NULL)
{
perror("malloc");
return 1;
}
//使用
int i = 0;
for (i = 0; i < 5; i++)
{
*(p + i) = i+1;
}
//希望将空间调整为40个字节
int*ptr = (int*)realloc(p, 40);
if (ptr != NULL) //调整成功
{
p = ptr;
int i = 0;
for (i = 5; i < 10; i++)
{
*(p + i) = i + 1;
}
for (i = 0; i < 10; i++)
{
printf("%d ", *(p + i));
}
free(p);//记得释放内存
p = NULL;
}
else //调整失败
{
perror("realloc");
free(p);//也要释放内存
p = NULL;
}
return 0;
}
//tips:realloc函数可以完成和malloc一样的功能
int main()
{
realloc(NULL, 20);//=== malloc(20);
return 0;
}
3.柔性数组(可长可短的数组)---------动态内存与结构体的配合
特点:
结构中的柔性数组放在结构体最后一个成员,且前⾯必须至少⼀个其他成员。
sizeof返回的这种结构大小不包括柔性数组的内存。
包含柔性数组成员的结构使用malloc函数进行内存的动态分配,并且分配的内存应该大于结构的大小,以适应柔性数组的预期大小。
struct S
{ char c;
int n;
int arr[];//柔性数组
};
int main()
{ struct S* ps = (struct S*)malloc(sizeof(struct S) + 5*sizeof(int));
if (ps == NULL)
{
perror("malloc");
return 1;
}
ps->n = 100;
int i = 0;
for (i = 0; i < 5; i++)
{
ps->arr[i] = i;
}
//调整空间
struct S* ptr = (struct S*)realloc(ps, sizeof(struct S)+10*sizeof(int));
if (ptr != NULL)
{
ps = ptr;
}
for (i = 0; i < 10; i++)//此时我们可以访问10个整型,不会越界
{
ps->arr[i] = i;
}
free(ps);//释放
ps = NULL;
return 0;
}
4.内存泄漏问题
大家可以来看看以下代码有没有什么问题
void test()
{
int *p = (int *)malloc(100);
if(NULL != p)
{
*p = 20;
}
}
int main()
{
test();
while(1);
}
return 0;
}
## 4.内存泄漏问题
大家可以来看看以下代码有没有什么问题
```c
void test()
{
int *p = (int *)malloc(100);
if(NULL != p)
{
*p = 20;
}
}
int main()
{
test();
while(1);
}
以上代码,p是局部变量,在函数里使用后就销毁了,你再也找不到他了,但是你又用它开辟了一段空间,并且没有释放,程序运行的时候就一直占用那片空间内存,你没法使用了,也就是造成了内存泄漏了。