为什么存在内存分配
我们之前学的内存分配,都是直接开辟内存。但是这样有一个问题:我们开辟的内存是大了还是小了,会不会浪费,会不会不够用。有的程序只有在运行的时候才知道要用多少空间。所以为了解决这样的问题,我们就有了内存的动态开辟。
动态内存都是在堆区上面的,外面操作动态内存的,malloc free calloc realloc 也都是对堆区的内存进行操作。
malloc 开辟空间 free 释放空间
malloc
malloc 是C语言提供的动态内存开辟函数。
viod* malloc(size_t size);
malloc 申请的是连续的空间。如果开辟成功,那么返回的就是开辟的内存的起始地址。如果开辟失败,就返回 NULL 。返回值的类型是 void* 因为 malloc 不知道开辟的内存是什么类型。所以我们使用的时候,可以强制类型转换一下。转换为我们要使用的类型。例如:我们开辟整型内存。
int* p = (int*)malloc(10*sizeof(int));
free
free 就是对开辟的堆区空间进行释放。每次 free 释放内存之后,一定要把指针变量置为 NULL 防止出现野指针。用法如下:
int* p = (int*)malloc(10*sizeof(int));
free(p);
p = NULL;
使用一个变量来开辟空间,这里使用一个变量 num 来开辟一段动态空间:
int main()
{
int num = 0;
scanf("%d", &num);
int* ptr = (int*)malloc(num * sizeof(int));
if (ptr != NULL)
{
int i = 0;
for (i = 0; i < num; i++)
{
*(ptr + i) = i + 1;
}
for (i = 0; i < num; i++)
{
printf("%d ", *(ptr + i));
}
}
free(ptr);
ptr = NULL;
return 0;
}
这里就是通过输入一个数 num 然后开辟相应的内存空间。并且赋值为 1 - 10 。
calloc 开辟空间
C语言也提供了另外一个开辟空间的函数,calloc 与 malloc 不同的是,calloc 开辟空间之后,在返回空间的起始地址之前,把所有空间全部赋值为 0 。示例:
void* calloc (void* ptr, size_t size);
int main()
{
int* p = (int*)calloc(10, sizeof(int));
if (NULL != p)
{
}
free(p);
p = NULL;
return 0;
}
由第二张图片就可以看到,p 所指向的空间,全部被置为了 0 。如果我们对申请的内存有初始化的需求,就可以使用 calloc 来进行初始化。
realloc 管理内存
有时候我们会发现申请的内存大了或者小了,所以就用到 realloc 来对内存大小进行灵活的调整。如果 realloc 调整内存失败后,也会返回空指针。C语言给的函数模型是这样:
void* realloc (void* ptr, size_t size);
ptr 是要调整的内存地址。 size 是要调整的大小。 返回值是调整大小之后的内存起始位置。
realloc 调整内存的两种情况
情况1:原有空间之后有足够大的空间
当原来的空间后面有足够大的空间的话,就连着原有空间继续开辟,就是下面图片这样的情况。就是原有内存之后直接追加空间,原来空间的数据不发生变化,并且返回值的地址还是原来的地址。
原有空间之后没有足够大的空间
当原来空间的后面没有足够大的空间的话,其扩展的方法就是:再找一块空间,可以放得下原有的空间的内容和新开辟的空间,并且返回新的内存地址。不过在传回新的地址之前。会把原来的那段空间 free 掉。不能用原来的指针去接收返回值,因为如果开辟失败,那么原来的指针也变成 NULL 了。就是下图这样:
realloc 使用代码示例
int main()
{
int* p = (int*)calloc(10, sizeof(int));
int* ptr = (int*)realloc(p, 20 * sizeof(int));
if (ptr != NULL)
{
p = ptr;
}
else
{
return -1;
}
for (int i = 10; i < 20; i++)
{
*(ptr + i) = i;
}
for (int i = 0; i < 20; i++)
{
printf("%d ", *(ptr + i));
}
free(ptr);
ptr = NULL;
free(p);
p = NULL;
return 0;
}
|