C语言—动态内存管理
文章目录
- C语言---动态内存管理
- 1. 为什么要进行动态内存分配
- 1.1 动态内存管理所在的区域
- 2. 动态内存函数的介绍
- 2.1 malloc
- 2.1.1 malloc语法
- 2.1.2 malloc具体实例
- 2.2 free
- 2.2.1 free语法
- 2.2.2 free具体实例
- 2.3 calloc
- 2.3.1 calloc语法
- 2.3.2 calloc具体实例
- 2.4 realloc
- 2.4.1 realloc语法
- 2.4.2 realloc调整空间时存在的情况
- 2.4.3 realloc具体实例
- 总结
1. 为什么要进行动态内存分配
我们已经掌握的内存开辟方式有:
int val = 20;//在栈空间上开辟四个字节
char arr[10] = {0};//在栈空间上开辟10个字节的连续空间
但是上述的开辟空间的方式有两个特点:
- 空间开辟大小是固定的。
- 数组在申明的时候,必须指定数组的长度,它所需要的内存在编译时分配。
但是对于空间的需求,不仅仅是上述的情况。有时候我们需要的空间大小在程序运行的时候才能知道,
那数组的编译时开辟空间的方式就不能满足了。这时候就只能试试动态存开辟了。
1.1 动态内存管理所在的区域
2. 动态内存函数的介绍
2.1 malloc
2.1.1 malloc语法
void* malloc (size_t size);
这个函数向内存申请一块连续可用的空间,并返回指向这块空间的指针。
- 如果开辟成功,则返回一个指向开辟好空间的指针。
- 如果开辟失败,则返回一个NULL指针,因此malloc的返回值一定要做检查。
- 返回值的类型是 void* ,所以malloc函数并不知道开辟空间的类型,具体在使用的时候使用者自己
来决定。 - 如果参数 size 为0,malloc的行为是标准是未定义的,取决于编译器。
2.1.2 malloc具体实例
代码如下(示例):
#include<errno.h>
#include<string.h>
#include<stdlib.h>
int main()
{
//申请40个字节,用来存放10个整型
int* p = (int*)malloc(40);
//malloc函数语法格式上返回类型是void* ,但原因是因为开发者为使用者留存的一个可以自己改变的类型的方式
//所以当使用malloc函数的时候,要清楚自己存放的类型是什么。同样要记住进行强制类型转换
if (p == NULL)
{
printf("%s\n", strerror(errno));
return 1;
}
//存放1--10
int i = 0;
for (i = 0; i < 10; i++)
{
*(p + i) = i + 1;
}
//打印
for (i = 0; i < 10; i++)
{
printf("%d ", *(p + i));
}
return 0;
}
2.2 free
2.2.1 free语法
void free (void* ptr);
free函数用来释放动态开辟的内存。
- 如果参数 ptr 指向的空间不是动态开辟的,那free函数的行为是未定义的。
- 如果参数 ptr 是NULL指针,则函数什么事都不做。
2.2.2 free具体实例
首先在上述malloc代码中存在一个隐患,就是在向内存空间申请40个字节的时候,如果代码结束运行之后操作系统会回收40个字节的空间,但是如果代码一直在运行,并且申请的40字节的内存已经用完不想在使用的时候,就会造成内存浪费,这个时候就需要free函数去释放申请的内存空间。
代码如下(示例):
#include<errno.h>
#include<string.h>
#include<stdlib.h>
int main()
{
//申请40个字节,用来存放10个整型
int* p = (int*)malloc(40);
//malloc函数语法格式上返回类型是void* ,但原因是因为开发者为使用者留存的一个可以自己改变的类型的方式
//所以当使用malloc函数的时候,要清楚自己存放的类型是什么。同样要记住进行强制类型转换
if (p == NULL)
{
printf("%s\n", strerror(errno));
return 1;
}
//存放1--10
int i = 0;
for (i = 0; i < 10; i++)
{
*(p + i) = i + 1;
}
//打印
for (i = 0; i < 10; i++)
{
printf("%d ", *(p + i));
}
//free是释放申请的内存
free(p);
p = NULL;//主动将释放后的p指针赋值NULL,防止非法访问
return 0;
}
2.3 calloc
2.3.1 calloc语法
void* calloc (size_t num, size_t size);
- 函数的功能是为 num 个大小为 size 的元素开辟一块空间,并且把空间的每个字节初始化为0。
- 函数 malloc 申请到的空间没有初始化,直接返回起始地址, calloc 会在返回地址之前把申请的空间的每个字节初始化为全0。
2.3.2 calloc具体实例
#include<errno.h>
#include<string.h>
#include<stdlib.h>
int main()
{
int* pi = (int*)malloc(40);
int* p = (int*)calloc(10, sizeof(int));
if (NULL == p)
{
perror("calloc");
return 1;
}
if (pi == NULL)
{
printf("%s\n", strerror(errno));
return 1;
}
//使用
int i = 0;
for (i = 0; i < 10; i++)
{
printf("%d\n", *(pi + i));
}
for (i = 0; i < 10; i++)
{
printf("%d ", *(p + i));
}
//释放
free(p);
free(pi);
p = NULL;
pi = NULL;
return 0;
}
2.4 realloc
2.4.1 realloc语法
有时会我们发现过去申请的空间太小了,有时候我们又会觉得申请的空间过大了,那为了合理的时候内存,我们一定会对内存的大小做灵活的调整。那 realloc 函数就可以做到对动态开辟内存大小的调整。
void* realloc (void* ptr, size_t size);
- ptr 是要调整的内存地址
- size 调整之后新大小
- 返回值为调整之后的内存起始位置
- 这个函数调整原内存空间大小的基础上,还会将原来内存中的数据移动到新的空间。
2.4.2 realloc调整空间时存在的情况
- 情况1:原有空间之后有足够大的空间
- 情况2:原有空间之后没有足够大的空间
- 当是情况1 的时候,要扩展内存就直接原有内存之后直接追加空间,原来空间的数据不发生变化。
- 当是情况2的时候,realloc函数会找一个满足空间大小的新的连续空间,把旧的空间的数据,拷贝新空间的前面的位置,并且把旧的空间释放,同时返回新的空间地址
- realloc如果扩容失败,就返回空指针
2.4.3 realloc具体实例
#include<errno.h>
#include<string.h>
#include<stdlib.h>
int main()
{
int* pi = (int*)malloc(5*sizeof(int));
if (NULL == pi)
{
perror("malloc");
return 1;
}
//使用
int i = 0;
for (i = 0; i < 5; i++)
{
*(pi + i) = 1;
}
//不够了,再增加5个整型
int* ptr = realloc(pi, 10 * sizeof(int));//需要用新的指针来接收,防止扩容失败把旧的数据弄丢
if (ptr != NULL)
{
pi = ptr;
}
//继续使用空间
for (i = 0; i < 10; i++)
{
printf("%d ", *(pi+ i));
}
//释放空间
free(pi);
pi = NULL;
return 0;
}
总结
- 在使用动态内存函数的时候要记得判断指针是都为NULL,否则容易出现问题。
- 在动态内存开辟之后,要及时使用free函数将其释放。