个人主页:@Weraphael
✍作者简介:目前是C语言学习者
✈️专栏:C语言航路
希望大家多多支持,咱一起进步!
如果文章对你有帮助的话
欢迎 评论 点赞 收藏 加关注
我们已经张伟的内存开辟方式有:
int a = 3;//在内存空间开辟4个字节
char a[10] = {0};//在内存开辟10个字节且连续的空间
但是上述的开辟空间的方式有两个特点:
- 空间开辟大小是固定的
- 数组再声明的时候,必须指定数组的长度,它所需要的内存在编译时分配。
但是对于空间的需求,不仅仅是上述的情况。因为有时候我们需要的空间大小在程序运行的时候才知道,那么以上的方式就不能满足了。而动态内存分配恰好能在需要时随时开辟,不需要的时候随时释放,且分配的大小就是程序要求的大小。
解读:
malloc
函数会向内存申请一块连续可用的空间,并返回指向这块空间的指针。
- 如果开辟成功,则返回一个指向开辟好的空间的指针
- 如果开辟失败,则返回一个
NULL
指针(空指针),因此malloc
函数的返回值一定要做检查
*返回值是void*
,表示的是无具体类型的指针,这说明malloc
函数并不知道开辟空间的类型,具体是在使用的时候让使用者来决定- 如果参数
size
为0,malloc
的行为是标准未定义的,取决用编译器
【malloc
函数实例】
strerror
讲解传送门 -> 点击此处
#include
#include //malloc函数所需头文件
#include //errno所需头文件
#include //strerror所需头文件
int main()
{
//假设向内存申请40个字节,用来存放整型
int* p = (int*)malloc(40);
//判断返回值是否为空指针
if (p == NULL)
{
//说明开辟内存空间失败了,那就显示为什么失败了
printf("%s\n", strerror(errno));
return 1;
}
//否则返回值就不为空指针
//假设存放数据1~10
for (int i = 0; i < 10; i++)
{
*(p + i) = 1 + i;
}
//打印
for (int i = 0; i < 10; i++)
{
printf("%d ", *(p + i));
}
return 0;
}
在
malloc
函数实例中,代码会存在隐患。malloc
函数既然在内存中申请了空间,用完了就应该还给操作系统。所以C语言提供了另外一个函数free
,专门用做动态内存的释放和回收的。
解读:
free
函数是用来释放内存块free
函数无返回值- 参数
ptr
是指向先前使用 malloc、calloc 或 realloc 分配的内存块的指针。- 如果参数
ptr
指向的空间不是动态开辟的,那么free
函数的行为就是未定义的- 如果参数
ptr
是NULL
指针,则函数不会做任何事。
【free
函数实例(完善malloc
函数实例)】
#include
#include //malloc函数所需头文件
#include //errno所需头文件
#include //strerror所需头文件
int main()
{
//假设向内存申请40个字节,用来存放整型
int* p = (int*)malloc(40);
//判断返回值是否为空指针
if (p == NULL)
{
//说明开辟内存空间失败了,那就显示为什么失败了
printf("%s\n", strerror(errno));
return 1;
}
//否则返回值就不为空指针
//假设存放数据1~10
for (int i = 0; i < 10; i++)
{
*(p + i) = 1 + i;
}
//打印
for (int i = 0; i < 10; i++)
{
printf("%d ", *(p + i));
}
//释放申请的内存
free(p);
p = NULL;
return 0;
}
释放完空间后,
p = NULL
的原因:
我们可以通过F10调试来观察
释放前:我们将数据1~10存放到内存了
释放后:我们发现,p在释放后还指向释放前的空间
C语言还提供一个函数
calloc
,calloc
函数也用来动态内存分配
解读:
- 参数
num
是开辟元素的个数- 参数
size
是每个元素的大小calloc
与malloc
区别在于calloc
会在返回地址之前把申请的空间的每个字节初始化为0
【calloc函数实例】
calloc
会将申请的空间全部初始化为0
#include
#include
#include
#include
int main()
{
//向内存申请10个元素,每个元素都是int类型
int* p = calloc(10, sizeof(int));
//判断p是否为空指针
if (p == NULL)
{
printf("%s\n", strerror(errno));
return 1;
}
//否则不是空指针
for (int i = 0; i < 10; i++)
{
printf("%d ", *(p + i));
}
//用完释放空间free
free(p);
p = NULL;
return 0;
}
realloc
函数的出现让动态内存管理更加灵活- 有时候会发现过去申请的空间太小了,有时候又会觉得申请的空间过大,那为了合理的使用内存,就要对内存的大小做灵活的调整。使用
realloc
函数就的功能就是调整动态开辟内存大小
解读:
- 参数
ptr
是要调整的内存地址- 参数
size
为调整之后的新大小- 返回值为调整之后的内存的起始地址
- 这个函数调整原内存空间大小的基础上,还会将原来内存中的数据移动带新的空间
realloc
函数在调整内存空间的时候存在两种情况:
- 当原有空间之后有足够大的空间,要扩展内存就直接在原有内存之后直接追加空间,原来空间的数据不发生变化,返回的是原来的地址
- 当原有空间之后没有足够大的空间,扩展的方法:在堆空间上另找一个合适大小的连续空间来使用。这样函数返回的是一个新内存的地址
【realloc函数实例】
#include
#include
#include
#include
int main()
{
//假设向内存申请20个字节来存放5个整型
int* p = (int*)malloc(20);
//判断p是否为空指针
if (p == NULL)
{
printf("%s\n", strerror(errno));
return 1;
}
//否则p就不是空指针
//接下来就将5个整型全部赋值成1
for (int i = 0; i < 5; i++)
{
*(p + i) = 1;
}
//那么接下来假设我还要再开辟5个空间(realloc)
int* ptr = (int*)realloc(p, 40);
if (ptr != NULL)//说明扩容成功
{
p = ptr;
}
//打印10个整型
for (int i = 0; i < 10; i++)
{
printf("%d ", *(p + i));
}
//释放空间(free)
free(p);
p = NULL;
return 0;
}
#include
#include
int main()
{
int* p = (int*)malloc(40);
*p = 20;
free(p);
p = NULL;
return 0;
}
- 若
p = NULL
,对于空指针解引用可能会导致程序异常终止或拒绝服务。
#include
#include
int main()
{
//向内存申请10个整型的空间
int* p = (int*)malloc(10 * sizeof(int));
if (p == NULL)
{
return 1;
}
for (int i = 0; i <= 10; i++)
{
*(p + i) = i;
}
free(p);
p = NULL;
return 0;
}
- 当指针指向的范围超过数组的范围时,p就是野指针,而野指针是没有访问权限的地址,会造成非法访问
#include
#include
int main()
{
int a = 10;
int* p = &a;
free(p);
p = NULL;
return 0;
}
- 这个就是
free
函数的概念,free
的参数指向的空间一定是动态开辟的,比如:malloc
、realloc
和calloc
。而这里的p
指向的是一个变量的地址
#include
#include
int main()
{
int* p = (int*)malloc(40);
p++;
free(p);
p = NULL;
return 0;
}
#include
#include
int main()
{
int* p = (int*)malloc(40);
if (p == NULL)
{
return 1;
}
free(p);
free(p);
return 0;
}
- 对一次
free
回收了p
申请的内存空间,此时的p
就是野指针,而野指针是没有访问地址的权限,若再一次使用free
来回收内存空间,可能会造成非法访问
#include
#include
int main()
{
int* p = (int*)malloc(40);
if (p == NULL)
{
return 1;
}
*p = 20;
return 0;
}
- 使用完内存空间后,并没有释放(free)内存空间,会导致内存泄漏