🌈大家好!我是Kevin,蠢蠢大一幼崽,很高兴你们可以来阅读我的博客!
🌟我热衷于分享🖊学习经验,🏫多彩生活,精彩足球赛事⚽
🌟感谢大家的支持,期待我们共同进步💞
🔗我的CSDN个人主页: Kevin ’ s blog
📂专栏收录:C预言
动态内存分配的意义
假如我们创建一个存放一百个元素的数组:
char arr[100] = {0};
如果我们要用这个数组来存储数据的话只能存储100个char型的数据,如果再想要往数组内添加数据的话就会越界。
所以在这个时候有两种方法,第一个方法是再创建一个数组然后将两个数组拼接,第二个方法时用动态内存分配。动态内存分配的快捷,实用性和可操控要比第一种方法强很多,那么究竟强在哪里呢?在这篇博客中我将进行具体阐述。
动态内存分配相关函数
1. malloc
malloc函数用来向内存申请⼀块连续可⽤的空间,并返回指向这块空间的指针。
该函数原型如下:
void* malloc (size_t size);
可以看出,该函数的返回值是void*说明返回类型可以改变,传的参数size作用是向内存申请size大小的内存,用size_t类型可以不用担心申请的内存过大而不能完成申请。
对于开辟内存的结果和操作:
如果开辟成功,则返回⼀个指向开辟好空间的指针。
如果开辟失败,则返回⼀个 NULL 指针,因此malloc的返回值⼀定要做检查。
返回值 的类型是 void* ,所以malloc函数并不知道开辟空间的类型,具体在使⽤的时候使⽤者⾃⼰来决定。
如果参数 size 为0,malloc的⾏为是标准是未定义的,取决于编译器
2. free
free函数用来释放动态分配的内存(只能释放动态分配的内存),函数原型如下:
void free (void* ptr);
如果参数 ptr 指向的空间不是动态开辟的,那free函数的⾏为是未定义的。
如果参数 ptr 是NULL指针,则无事发生。
在了解到malloc和free函数后就可以利用这两个函数进行一个简单的实例代码:
#include <stdio.h>
#include <stdlib.h>
int main()
{
int num = 0;
scanf("%d", &num);
int arr[num] = {0};
int* ptr = NULL;
ptr = (int*)malloc(num*sizeof(int));
if(NULL != ptr)//判断ptr指针是否为空
{
int i = 0;
for(i=0; i<num; i++)
{
*(ptr+i) = 0;
}
}
free(ptr);//释放ptr所指向的动态内存
ptr = NULL;//是否有必要?
return 0;
}
该段代码就是实现动态分配,然后释放分配的空间。
3. calloc
calloc 函数也⽤来动态内存分配。原型如下:
void* calloc (size_t num, size_t size);
函数的功能是为 num 个⼤⼩为 size 的元素开辟⼀块空间,并且把空间的每个字节初始化为0。
与函数 malloc 的区别只在于 calloc 会在返回地址之前把申请的空间的每个字节初始化为全 0.
示例代码:
#include <stdio.h>
#include <stdlib.h>
int main()
{
int* p = (int*)calloc(10, sizeof(int));
if (NULL != p)
{
int i = 0;
for (i = 0; i < 10; i++)
{
printf("%d ", *(p + i));
}
}
free(p);
p = NULL;
return 0;
}
示例代码结果:
0 0 0 0 0 0 0 0 0 0
从函数的特性出发,如果我们对函数有初始化的需求的话可以直接使用 calloc 函数来进行内存空间的开辟,效果与 malloc 一样。
4. realloc
realloc 函数实现的是对原本开辟的空间进行调整大小,函数原型如下:
void* realloc (void* ptr, size_t size);
ptr 是要调整的内存地址
size 调整之后新⼤⼩
返回值为调整之后的内存起始位置。
这个函数调整原内存空间⼤⼩的基础上,还会将原来内存中的数据移动(copy)到新的空间。
realloc在调整内存空间的是存在两种情况:
情况1:原有空间之后有⾜够⼤的空间
情况2:原有空间之后没有⾜够⼤的空间
以上所说两种空间是在内存上的空间,不是当前动态分配到的空间
4.1 原有空间足够大
当原有空间足够大时,我们要扩展内存就直接原有内存之后直接追加空间,原来空间的数据不发⽣变化。
4.2 原有空间不够
当原有的空间不够时,我们在堆空间上另找⼀个合适大小的连续空间来使⽤。这样函数返回的是⼀个新的内存地址。
realloc使用示例代码:
#include <stdlib.h>
int main()
{
int* ptr = (int*)malloc(100);
if (ptr != NULL)
{
//业务处理
}
else
{
return 1;
}
//扩展容量
//代码1 - 直接将realloc的返回值放到ptr中
ptr = (int*)realloc(ptr, 1000);//这样可以吗?(如果申请失败会如何?)
//代码2 - 先将realloc函数的返回值放在p中,不为NULL,在放ptr中
int* p = NULL;
p = realloc(ptr, 1000);
if (p != NULL)
{
ptr = p;
}
//业务处理
free(ptr);
return 0;
}
常见动态内存错误
1. 对NULL指针解引用
void test()
{
int* p = (int*)malloc(INT_MAX / 4);
*p = 20;//如果p的值是NULL,就会有问题
free(p);
}
2. 对动态开辟空间的越界访问
void test()
{
int i = 0;
int* p = (int*)malloc(10 * sizeof(int));
if (NULL == p)
{
exit(EXIT_FAILURE);
}
for (i = 0; i <= 10; i++)
{
*(p + i) = i;//当i是10的时候越界访问
}
free(p);
}
3. 对⾮动态开辟内存使⽤free释放
void test()
{
int a = 10;
int *p = &a;
free(p);//error
}
4. 使⽤free释放⼀块动态开辟内存的⼀部分
void test()
{
int *p = (int *)malloc(100);
p++;
free(p);//p不再指向动态内存的起始位置
}
5. **对同⼀块动态内存多次释放 **
void test()
{
int* p = (int*)malloc(100);
free(p);
free(p);//重复释放
}
6. 动态开辟内存忘记释放(内存泄漏)
void test()
{
int* p = (int*)malloc(100);
if (NULL != p)
{
*p = 20;
}
}
int main()
{
test();
while (1);
}
结语
以上就是关于动态内存管理的博客内容。值得强调的是关于 free 掉内存这件事,一个程序如果不在不需要的时候free掉内存的话就会一直占用内存直到程序运行结束。
在我们平时的代码练习中不会有明显的影像,但是在大型程序中,如果内存一直占用,占用的内存不断增多,内存是有限的,不可能一直被占用,当内存爆满时程序就会出现问题了。
所以要注意关于内存空间的释放!