一、动态内存函数
动态内存都是向堆区申请空间,在堆区上进行各种操作。这些函数都包含在头文件 <stdlib.h> 中。
malloc
malloc函数:动态开辟内存,但不初始化。
void* malloc(size_t size);
参数:size 表示的是 要开辟内存的大小,单位是字节(size_t 是无符号整型类型)。
返回类型:malloc函数返回类型是 void* 类型,所以实际使用时要根据情景来强制转换类型,并且需要使用一个指针来接收返回值。
注:malloc 申请内存可能会失败。若malloc 申请内存成功,返回申请的那块内存的首地址;若申请失败,则返回NULL。所以内存申请后,要检验是否申请成功。
free
free函数:释放动态开辟的内存。
void free(void* ptr);
参数:可以接收任意类型的指针,ptr 表示 那个接收动态内存首地址的指针。
返回类型:空类型,无返回值。
注:1、free函数不能用来释放非动态开辟的内存空间。
2、如果参数 ptr 是NULL指针,则free函数不进行任何操作。
malloc 与 free 要搭配使用
int main()
{
int* p = (int*)malloc(100); //申请了100个字节,即 25个整型
if (p == NULL) //判断内存是否申请成功
{
printf("Failure\n");
return 1;
}
//当这些空间不用了,就要释放掉
free(p);
p = NULL; //为防止野指针,指针释放后要被置空
return 0;
}
calloc
calloc函数:动态开辟内存,并把内存的每个字节都初始化为0。
void* calloc(size_t num, size_t size);
参数:num 代表 要申请的元素的数量,size 代表 每个元素的大小(单位字节)。
返回类型:void*,同样需要用指针来接收。
realloc
realloc函数:改变已经申请的动态内存的大小。
void* realloc(void* ptr, size_t size);
参数:ptr 是要调整的内存的首地址,size 是调整后的新大小(单位字节)。
realloc 和 calloc 也需要进行检验,判断是否成功。
对于realloc的申请有不同的情况:
若申请成功有两种情况,第一种是原空间后面有足够的内存,新增的空间直接在后面续上,然后返回旧的地址;第二种是原空间后没有足够的内存用来扩容,那么realloc会寻找一个新的满足扩容后大小的空间,然后把旧空间内的数据拷贝到新空间前面对应的位置上,并把旧空间释放,同时返回新空间的首地址。
若申请失败,则返回NULL,用原指针来接收可能会导致数据丢失,所以realloc要新建一个指针来接收,不能直接用原指针接收扩容后的内存。
注:realloc接收的指针是空指针时相当于是malloc。
realloc的使用示例:
#include <stdio.h>
#include <stdlib.h>
int main()
{
int* p = (int*)malloc(16);
if (p == NULL) //判断malloc是否申请成功
{
printf("Failure\n");
return 1;
}
//用realloc扩容
int* pa = (int*)realloc(p,40); //用新指针接收,扩容为40字节
if (pa == NULL) //判断realloc是否扩容成功
{
printf("Failure\n");
return 1;
}
p = pa; //成功后再把空间赋给原指针
pa = NULL;
//用完后释放并指针置空
free(p);
p = NULL;
return 0;
}
二、常见的动态内存错误
-
对NULL指针的解引用操作
出现这种错误,一般是忘记检验内存申请是否成功就直接使用接收的指针。 -
对动态开辟空间的越界访问
这个错误要注意的是,三个动态内存申请函数的参数的单位,区分开字节和元素个数,同时注意动态申请的内存与数组类似,首元素从下标0开始。 -
对非动态开辟内存使用free释放
free不能用于接收非动态开辟内存的指针。 -
使用free释放一块动态开辟内存的一部分
void test()
{
int *p = (int*)malloc(100);
p++;
free(p);//p不再指向动态内存的起始位置
}
此时释放的就是开辟内存的一部分,所以接收地址的原指针不要随意更改,要更改地址来实现一些功能就新建一个指针,拷贝一下再用。
- 对同一块动态内存多次释放
void test()
{
int *p = (int *)malloc(100);
free(p);
free(p);//重复释放
}
同一块动态内存只能释放一次。
- 动态开辟内存忘记释放(内存泄漏)
内存泄漏的问题还是很严重的,所以动态开辟的内存一定要及时用free释放,使用了动态内存函数的地方要用注释标注,防止忘记释放。