文章目录
为什么需要动态内存分配
大多数的时候我们使用的内存空间开辟方式有:
int i = 5; //在栈空间开辟4字节,将5存放在这块内存空间
char arr[10] = { 0 }; //在栈上连续开辟10字节
在我们最开始声明一个数组时,必须要使用一个常量来指定数组的长度。但是在没有真正使用这个数组的时候,并不知道具体要开辟多大的内存空间,它所需要的空间取决于输入的数据。假设在一个程序中实际上我们只需要10字节,而声明的时候仅仅指定了4字节的空间,就会出现空间不足或者溢出;如果指定了10000字节或者更大的空间则会形成浪费。此时就可以使用动态内存分配了。
动态内存函数介绍
malloc、free
C语言为动态内存的分配提供了两个函数malloc和free,分别用于动态内存的分配和释放。
//malloc函数原型
void* malloc(size_t sz);
作用:开辟出 sz 字节的内存空间
所需头文件:stdlib.h 和 malloc.h
malloc
参数:所需开辟内存的大小,单位为字节
返回值:如果开辟成功了,则返回指向成功开辟的内存首地址的指针。否则返回空指针
malloc函数在开辟空间的时候是不对其赋值的,所以在使用之前赋值是很重要的!它开辟的是一块连续的内存空间,不会分开去开辟。另外还有一些编译器,在你使用malloc函数申请内存的时候,实际分配的又可能会比我们请求的要多一些。最后需要避免一个误区,在使用malloc开辟的内存之前,需要判断是否开辟成功,也就是判断返回的是不是空指针,因为我们不能对空指针进行解引用。
总结一下:
- 开辟成功,返回一个指向开辟好的空间的指针。
- 如果开辟失败,返回空指针
- 返回值的类型为void*,malloc函数不知道开辟空间的类型,具体的类型有环境决定
- malloc开辟空间时不对其初始化,使用前要赋值
- 使用前判断是否为空指针,不能对空指针解引用操作
- 如果机器要求边界对齐,返回的内存的起始地址要满足边界对齐的要求
//free函数原型
void free(void* ptr);
作用:释放动态内存
所需头文件:stdlib.h 和 malloc.h
free
参数:指向以前使用malloc、calloc或realloc分配的内存块的指针。
返回值:无
使用malloc函数开辟的内存之后,需要对这块内存进行回收。
- free参数ptr指向的空间必须时动态开辟出来的
- 参数ptr 如果是空指针,则函数什么事都不做
- 使用free函数释放空间后,需要置成空指针
例如:
# include <stdio.h>
# include <stdlib.h>
# include <malloc.h>
int main(void)
{
int* p = NULL;
p = (int*)malloc(20); //malloc返回的是void*,需要强转成需要的类型
//使用
if (p == NULL)
{
printf("开辟失败");
}
else
{
for (int i = 0; i < 10; i++)
{
*(p+i) = i;
}
}
//使用完之后需要释放
free(p);
p = NULL; //释放了并不代表不在,置成空指针,防止变成野指针
return 0;
}
calloc、realloc
C语言中还提供了calloc 和 realloc函数来进行动态内存分配
//calloc函数原型
void* calloc(size_t num, size_t size);
作用:为num个大小为size的元素开辟一块内存空间,并且把内存空间的每个字节初始化为0,size_t是无符号整型。
所需头文件:stdlib.h
# include <stdio.h>
# include <stdlib.h>
int main(void)
{
int* p = NULL;
p = calloc(10, sizeof(int)); //为10个int字节大小的元素分配内存空间
if (p == NULL)
{
printf("开辟失败\n");
}
else
{
//使用
for (int i = 0; i < 10; i++)
{
//将内存空间的每一个字节都初始化为0,所以会打印10个0
printf("%d ", *(p + i));
}
}
free(p);
p = NULL;
return 0;
}
所以当我们在动态内存开辟时,如果不需要初始化则使用malloc,如果想要开辟出的内存空间的每一个字节都初始化为0时,则使用calloc.
relloc
//realloc函数原型
void* realloc(void* ptr, size_t size);
作用:为之前动态申请的ptr所指向的内存空间,重新调整至sz个字节,并返回调整之后的内存起始地址。size_t是无符号整型
所需头文件:stdlib.h
我们需要注意:
示范:
# include <stdio.h>
# include <stdlib.h>
# include <malloc.h>
int main(void)
{
int* p = NULL;
p = (int*)malloc(10);
if (p == NULL)
{
printf("内存开辟失败!\n");
return 0;
}
for (int i = 0; i < 10; i++)
{
*(p + i) = i;
}
//将p所指向的内存空间大小,调整到30字节
int* pf = (int*)realloc(p, 30);
if (pf != NULL)
{
for (int i = 10; i < 20; i++)
{
*(pf + i) = i;
printf("%d ", *(pf + i));
}
}
//释放
free(pf);
pf = NULL;
return 0;
}
常见的动态内存错误
在使用动态内存分配时,经常出现很多的小错误。
对NULL指针进行解引用
动态内存分配最常见的错误就是没有检查所请求的内存空间是否分配成功,对于这些动态内存分配函数,当开辟失败时,它们都会返回一个空指针。我们是不能对没有初始化的指针和空指针进行解引用操作的!
对动态开辟的内存越界访问
这个错误就是在我们操作内存时超出了分配内存的边界
对非动态内存开辟的内存使用free释放
使用完动态开辟的内存后忘记释放
当我们在一个程序中使用完动态分配的内存后,一定要记得释放和回收,这样它才能被重新分配。如果使用完之后不进行释放,它既不使用,也不让别人使用。那么这块内存空间就像屁一样的存在,一点用都没有,相当于泄露了一样。因此这种情况被称为内存泄漏