文章目录
- 前言
- 一、为什么存在动态内存管理
- 二、动态内存函数的介绍
- 1. malloc函数
- 2. 内存泄漏
- 3. 动态内存开辟位置
- 4. free函数
- 5. calloc 函数
- 6. realloc 函数
- 7. realloc 传空指针
- 总结
前言
C语言动态内存管理malloc、calloc、realloc、free函数、内存泄漏、动态内存开辟的位置等的介绍
一、为什么存在动态内存管理
int a = 0;
int arr[5] = {0};
- 上述的开辟空间的方式有两个特点:
- 空间开辟大小是固定的。
- 数组在声明的时候,必须指定数组的长度,它所需要的内存在编译时分配。
但是对于空间的的需求,不仅仅是上述的情况
有时候,我们需要的空间大小在程序运行的时候才能知道,上述的编译时开辟空间的方式就不能满足了。
这时候就只能用动态内存开辟了。
二、动态内存函数的介绍
1. malloc函数
- malloc 函数需要引入头文件 <stdlib.h>
C语言提供了一个动态内存开辟的函数:
void* malloc(size_t size);
这个函数向内存申请了一块连续可用的空间,并返回指向这块空间的指针。
- 如果开辟成功,则返回一个指向开辟好空间的指针。
- 如果开辟失败,则返回一个NULL指针,因此malloc的返回值一定要做检查。
- 返回值的类型是 void* ,所以malloc函数并不知道开辟空间的类型,具体在使用的时候使用者自己来决定。
- size 指的是 size 个 字节 的大小的空间
- 如果参数 size 为 0, malloc 的行为是标准未定义的,取决于编译器。
- 动态开辟40个字节大小的空间
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
int arr[10] = { 0 };
int* p = (int*)malloc(40); // malloc 开辟 40 个字节大小的内存空间
// 检验如果 动态内存开辟失败,打印错误信息,终止程序
if (NULL == p)
{
printf("%s\n", strerror(errno));
return 1;
}
int i = 0;
for (i = 0; i < 10; i++)
{
*(p + i) = i;
}
for (i = 0; i < 10; i++)
{
printf("%d ", *(p + i)); // 0 1 2 3 4 5 6 7 8 9
}
return 0;
}
- 需要开辟的内存空间过大返回空指针
INT_MAX 是定义的一个 21亿多的一个数字
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
int arr[10] = { 0 };
int* p = (int*)malloc(INT_MAX * 2); // malloc 开辟 INT_MAX * 2 个字节大小的内存空间
// 检验如果 动态内存开辟失败,打印错误信息,终止程序
if (NULL == p)
{
printf("%s\n", strerror(errno));
return 1;
}
int i = 0;
for (i = 0; i < 10; i++)
{
*(p + i) = i;
}
for (i = 0; i < 10; i++)
{
printf("%d ", *(p + i)); // Not enough space
}
return 0;
}
2. 内存泄漏
- 如果一个程序在向内存申请空间后,没有释放申请的空间,则在程序结束的时候,系统会自动回收内存空间
- 如果一个程序在向内存申请空间后,没有释放申请的空间, 并且程序在持续运行,短时间不结束,此时就存在内存泄漏。
3. 动态内存开辟位置
- 动态内存开辟在堆区。
- 局部变量等开辟在栈区。
4. free函数
C语言提供了一个函数free,专门是用来做动态内存的释放和回收的
void free (void* ptr);
free 函数是用来释放动态开辟的内存。
- 如果参数 ptr 指向的空间不是动态开辟的,那free函数的 行为是未定义的。
- 如果参数 ptr 是 NULL 指针,则函数什么事都不做。
- malloc 和 free 函数都声明在 <stdlib.h>头文件中。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
int arr[10] = { 0 };
int* p = (int*)malloc(40); // malloc 开辟 40 个字节大小的内存空间
// 检验如果 动态内存开辟失败,打印错误信息,终止程序
if (NULL == p)
{
printf("%s\n", strerror(errno));
return 1;
}
int i = 0;
for (i = 0; i < 10; i++)
{
*(p + i) = i;
}
for (i = 0; i < 10; i++)
{
printf("%d ", *(p + i)); // 0 1 2 3 4 5 6 7 8 9
}
free(p);
p = NULL;
return 0;
}
- free (p) 就会 将申请40个字节的动态内存空间 释放掉。
- 但是 p 依然存放着 之前接收的动态内存的地址,会变成野指针。
- 所以 在 释放完p 的空间后 将p 重置为 NULL指针。
5. calloc 函数
C语言还提供另外一个叫 calloc,calloc,函数也用来动态内存分配。
void* calloc(size_t num, size_t size);
- 函数功能是为num个大小为size的元素开辟一块空间,并且把空间的每个字节初始化为0。
- 与函数 malloc 的区别只在于 calloc 会在返回地址之前把申请的空间的每个字节初始化为全0。
- 需要初始化时,用 calloc, 不需要初始化 用 malloc。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
int* p = (int*)calloc(10, sizeof(int)); // 开辟 10 个 int类型大小 的空间
if (NULL == p)
{
printf("%s\n", strerror(errno));
return 1;
}
int i = 0;
for (i = 0; i < 10; i++)
{
// 自动初始化为全 0
printf("%d ", *(p + i)); // 0 0 0 0 0 0 0 0 0 0
}
free(p);
p = NULL;
return 0;
}
6. realloc 函数
- realloc 函数的出现让动态内存管理更加灵活
- realloc函数可以做到对动态开辟内存大小的调整
void* realloc(void* ptr, size_t size);
- ptr 是要调整的内存地址
- size 调整之后新大小
- 返回值为调整之后的内存起始位置。
- 这个函数调整原内存空间大小的基础上,还会将原来内存中的数据移动到新的空间。
- realloc在调整内存空间的时候存在两种情况:
- 1: 原有空间之后有足够大的空间。
- 2: 原有空间之后的空间不够或被占用。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
int* p = (int*)malloc(40); // 开辟 10 个 int类型大小 的空间
if (NULL == p)
{
printf("%s\n", strerror(errno));
return 1;
}
int i = 0;
// 使用
for (i = 0; i < 10; i++)
{
*(p + i) = i + 1;
}
// 扩容
int* str = (int*)realloc(p, 80);
if (str != NULL)
{
p = str;
}
// 使用
for (i = 10; i < 20; i++)
{
*(p + i) = i + 1;
}
for (i = 0; i < 20; i++)
{
printf("%d ", *(p + i));
// 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
}
free(p);
p = NULL;
return 0;
}
7. realloc 传空指针
- realloc传空指针 和 malloc是等价的。
#include <stdio.h>
int main()
{
int* p = realloc(NULL, 40);
// 此时 realloc 和 malloc(40)是等价的
return 0;
}
总结
C语言动态内存管理malloc、calloc、realloc、free函数、内存泄漏、动态内存开辟的位置等的介绍