动态内存管理目录
- 🤔 前言 🤔
- 📕 malloc 函数【动态内存开辟函数】 📕
- 📚 free 函数【动态内存释放函数】
- 📗 calloc 函数【动态内存开辟并初始化为0的函数】📗
- 📘 realloc 函数【动态内存重分配函数】 📘
- 👾 常见的动态内存错误 👾
- 🏁 总结 🏁
🤔 前言 🤔
之前我们学会了在内存中开辟一块空间(也就是定义一个变量或数组),但是这些都是比较死板的。比如我们如果使用一个数组,当我们的需求量大于数组的空间时,数组空间远大于实际需求时,这个弊端就体现出来了。不太符合实际开发需求,在很多的在线OJ题目上也是要求使用动态内存来解题,这时我们就需要学会C语言给我们提供的动态内存管理函数。
📕 malloc 函数【动态内存开辟函数】 📕
🌄 malloc 函数的声明 🌄
🌅 malloc 函数的参数列表 🌅
可以看到 malloc 函数的参数列表是只有一个 size_t size
而对于size的描述是这样的:
翻译:
所以在使用的时候只需要将需要的空间大小传给函数就行了。
🌠 malloc 函数的返回值 🌠
可以再函数声明的地方看到,函数返回的值是 void*
类型的指针,其具体的声明如下:
翻译:
⚓️ malloc 函数的使用注意事项⚓️
- 因为函数的返回类型是 void* 类型的只合作呢,所以我们在使用函数的时候需要将函数的返回值强制类型转换为我们需要的类型。
int* ptr = (int*)malloc(sizeof(int) * 4); //开辟四个整型空间的大小
- 当函数没有成功开辟空间时返回的是空指针,所以在使用这块空间之前需要判断这个函数是否是空指针。
int* ptr = (int*)malloc(sizeof(int) * 4);
if (ptr == NULL) { //判断该空间是否成功开辟
perror("malloc"); //打印程序的错误信息
return; // 如果开辟空间失败结束程序
}
- 使用后需要将开辟的空间释放,并将指针置为空指针,防止内存泄漏和野指针的使用。
free(ptr): //释放开辟的动态空间
ptr = NULL; //置为空指针
- malloc函数开辟的动态空间初始值都是随机值。
📚 free 函数【动态内存释放函数】
🌄 free 函数的声明 🌄
🌅 free 函数的参数列表 🌅
free 函数的只有一个 void*
类型的指针
- ptr
⚓️ free 函数的使用注意事项⚓️
- 当ptr为NULL时
- 如果ptr 指向的不是动态内存时
📗 calloc 函数【动态内存开辟并初始化为0的函数】📗
🌄 calloc 函数的声明 🌄
🌅 calloc 函数的参数列表 🌅
在calloc函数的参数列表中有两个 size_t 类型
的参数:
这两个参数的意思分别是:
- num
- size
所以在使用的时候只需要将元素的大小和元素个数传个函数就行了。
🌠 calloc 函数的返回值 🌠
该函数的返回值和malloc的返回值一模一样:
翻译:
⚓️ calloc 函数的使用注意事项⚓️
- 因为函数的返回类型是 void* 类型的只合作呢,所以我们在使用函数的时候需要将函数的返回值强制类型转换为我们需要的类型。
int* ptr = (int*)calloc( 4, sizeof(int) ); //开辟四个整型空间的大小
- 当函数没有成功开辟空间时返回的是空指针,所以在使用这块空间之前需要判断这个函数是否是空指针。
int* ptr = (int*)malloc( 4, sizeof(int) );
if (ptr == NULL) { //判断该空间是否成功开辟
perror("calloc"); //打印程序的错误信息
return; // 如果开辟空间失败结束程序
}
- 使用后需要将开辟的空间释放,并将指针置为空指针,防止内存泄漏和野指针的使用。
free(ptr): //释放开辟的动态空间
ptr = NULL; //置为空指针
- calloc函数开辟的动态空间初始值都是0。
📘 realloc 函数【动态内存重分配函数】 📘
realloc 函数可以说是动态内存管理函数中最能体现动态内存管理的函数了,因为 malloc 和 calloc 这两个函数都是只能管理分配多少个空间,不能将空间大小改变,这就跟我们之前学过得内存开辟有点相似。所以需要使用 realloc 函数来实现动态的内存管理。
🌄 realloc 函数的声明 🌄
🌅 realloc 函数的参数列表 🌅
realloc函数有两个参数,一个是在 void*
类型的指针,还有一个是 size_t
类型的参数。
这两个参数的意思分别是:
- ptr
- size
所以在使用的时候需要将需要改变大小的动态内存空间和需要的大小传给 realloc 函数。
🌠 realloc 函数的返回值 🌠
- 正常的情况下:
- 当size为0时:
- 当函数未能分配请求的内存块时:
⚓️ realloc 函数的使用注意事项⚓️
- 传入的参数必须是动态开辟的空间,也就是说必须是使用 malloc 、realloc或者 calloc函数开辟的动态空间才可以使用 realloc 函数来重置空间大小
int* ptr = (int*)malloc(sizeof(int)*4);
if (ptr == NULL) {
perror("malloc");
return 0;
}
int* ptr1 = realloc(ptr, sizeof(int) * 8); //ptr必须是动态开辟的空间
- 使用的时候前需要对所开辟的空间进行判断,看其是否成功分配内存
if (ptr1 == NULL) {
perror("realloc");
return 0;
}
- 使用后需要将开辟的空间释放,并将指针置为空指针,防止内存泄漏和野指针的使用。
free(ptr1);
ptr1 = NULL;
- realloc 函数开辟的空间的初始值都是随机值
- 当realloc 函数的size为0时,其作用跟free函数一样
realloc(ptr, 0) == free(ptr)
👾 常见的动态内存错误 👾
在使用动态内存的时候我们可能会在不知不觉中犯一些不易察觉的错误,所以我们在使用动态内存的时候需要严谨点。
⚠️ 对NULL指针的解引用操作 ⚠️
int *p = (int *)malloc(INT_MAX/4);
*p = 20;//如果p的值是NULL,就会有问题
free(p);
int *p = (int *)malloc(INT_MAX/4);
if (p == NULL) { //进行判空
perror("malloc"); //如果是空指针,就打印错误
}
*p = 20;
free(p);
⚠️ 对动态开辟空间的越界访问 ⚠️
int i = 0;
int *p = (int *)malloc(10*sizeof(int));
if(NULL == p)
{
perror("malloc");
}
for(i=0; i<=10; i++)
{
*(p+i) = i;//当i是10的时候越界访问
}
free(p);
```c
int i = 0;
int *p = (int *)malloc(10*sizeof(int));
if(NULL == p)
{
perror("malloc");
}
for(i=0; i<10; i++) // 控制i不超过空间的大小
{
*(p+i) = i;//当i是10的时候越界访问
}
free(p);
⚠️ 对非动态开辟内存使用free释放 ⚠️
**void test()
{
int a = 10;
int *p = &a;
free(p);//ok?
}**
所以我们在使用free 函数的时候千万不要将非动态开辟的的内存释放了。
⚠️ 使用free释放一块动态开辟内存的一部分 ⚠️
void test()
{
int *p = (int *)malloc(100);
p++;
free(p);//p不再指向动态内存的起始位置
}
void test()
{
int *p = (int *)malloc(100);
int *ptr = p;
ptr++;
free(p); //p还是指向动态内存的起始位置
p = NULL; //使用完后置空,防止野指针的使用
ptr = NULL;
}
⚠️ 对同一块动态内存多次释放 ⚠️
void test()
{
int *p = (int *)malloc(100);
free(p);
free(p);//重复释放
}
void test()
{
int *p = (int *)malloc(100);
free(p);
}
⚠️ 动态开辟内存忘记释放(内存泄漏) ⚠️
void test()
{
int *p = (int *)malloc(100);
if(NULL != p){
*p = 20;
}
}
int main()
{
test();
while(1);
}
在运行程序前,先打开任务管理器查看一下CPU利用率:
- 内存泄漏
所以我们使用完动态开辟的空间之后一定要正确得释放空间。
void test()
{
int *p = (int *)malloc(100);
if(NULL != p){
*p = 20;
}
free(p);
p = NULL;
}
int main()
{
test();
while(1);
}
🏁 总结 🏁
总得来说,动态内存在给我们便利的时候也给我们制造了一定的麻烦,所以要正确得选择工具。
-
在使用动态内存空间时需要注意正确开辟,在使用前进行判断是否开辟成功。
-
根据实际需要选取malloc函数和calloc函数。
-
使用的时候注意不要越界访问。
-
使用后需要将态开辟的空间正确释放。
-
要注意防范野指针的使用。