动态内存管理
1.为什么要进行动态内存分配?
int a = 10;
char arr[20] = {0};
以上是我们所掌握的内存开辟方式,该种开辟方式具有以下特点:
- 空间开辟大小固定。
- 数组在声明时必须指定长度。
但对于空间的需求,有时所需空间的大小只有在程序运行时才知道,那上述数组的开辟空间方式就不能满足了。
这时候就需要进行动态内存开辟
2.动态内存函数
2.1 malloc
#include <stdlib.h>
void* malloc(size_t size);
该函数向内存申请了一块大小为size字节的连续可用的空间。
-
若开辟成功,则返回新申请空间的指针
-
若开辟失败,则返回空指针NULL
-
返回值类型为void*,使用时需要使用者自行进行类型转换,例如:
int main() { malloc(20); int* p = (int*)malloc(20); return 0; }
2.2 free
#include <stdlib.h>
void free(void* ptr);
该函数用来释放动态开辟的空间,例如:
int main()
{
int* p = (int*)malloc(20);
free(p);
p = NULL;//释放完空间后,p仍指向原本空间,为保证安全,应主动将p置为空
return 0;
}
2.3 calloc
#include <stdlib.h>
void* calloc(size_t num,size_t size)
该函数开辟num个以size为大小的空间,并将空间中每个字节初始化为0。
与malloc函数一样:
-
若开辟成功,则返回新申请空间的指针
-
若开辟失败,则返回空指针NULL
-
返回值类型为void*使用时需要使用者自行进行类型转换
2.4realloc
void* realloc (void* ptr,size_t size);
- ptr为要调整的内存地址
- size为调整后的大小
- 返回值为调整后的内存起始位置
该函数可以做到对动态开辟内存大小的调整,使动态内存管理更加灵活,例如:
int main()
{
int* p = (int*)malloc(20);
realloc(p,40);
return 0;
}
但需要注意的是,realloc的调整内存空间有两种情况:
- 若原空间后有足够大的内存空间,则会直接在原内存后拓展新的空间,原来空间的数据不会发生变化
- 若原空间后没有足够大的内存空间,则会在堆区另找合适的空间来使用,并将原空间的数据拷贝到新空间
3.常见的动态内存错误
结合上述各个动态内存函数特点,以下是我们在编写代码时可能会遇到的错误:
3.1对NULL指针的解引用
int main()
{
int* p = (int*)malloc(20);
int i = 0;
for (i = 0; i < 5; i++)
{
p[i] = i;
}
free(p);
p = NULL;
return 0;
}
- 上述代码中malloc函数可能申请空间失败,因此在使用p之前,需要先判断p是否为NULL
3.2对动态开辟空间进行越界访问
int main()
{
int* p = (int*)malloc(20);
if (p == NULL)
{
printf("内存空间开辟失败\n");
return 1;
}
int i = 0;
for (i = 0; i < 10; i++)
{
p[i] = i;
}
free(p);
p = NULL;
return 0;
}
- 上述代码只申请了20个字节的空间,却非法访问了4*10=40个字节的空间
3.3对非动态开辟的内存进行free释放
int main()
{
int arr[10] = { 1,2,3,4,5 };
int* p = arr;
free(p);
p = NULL;
return 0;
}
- 上述代码对非动态开辟的内存进行了free释放,程序会报错
3.4使用free释放一块动态开辟内存的一部分
int main()
{
int* p = (int*)malloc(40);
if (p == NULL)
{
printf("内存空间开辟失败\n");
return 1;
}
int i = 0;
//[1] [2] [3] [4] [5] [ ] [ ] [ ] [ ] [ ]
for (i = 0; i < 5; i++)
{
*p = i + 1;
p++;
}
free(p);
p = NULL;
return 0;
}
- 上述代码只free释放了p+4到p+9的动态开辟内存空间,程序执行时仍会报错
3.5对同一块动态内存多次释放
int main()
{
int*p = (int*)malloc(20);
if (p == NULL)
{
return 1;
}
free(p);
//释放
free(p);
p = NULL;
return 0;
}
- 上述代码对同一块内存空间进行了多次释放,程序会报错
3.6动态开辟内存忘记释放(内存泄漏)
void test()
{
int* p = (int*)malloc(20);
}
int main()
{
test();
return 0;
}
- 上述代码在test函数内申请完内存空间没有进行free释放,该动态内存空间会一直存在于堆区,直至程序结束操作系统主动回收该块空间。虽然程序可以正常运行,但在程序结束前,该空间被浪费
存空间进行了多次释放,程序会报错
3.6动态开辟内存忘记释放(内存泄漏)
void test()
{
int* p = (int*)malloc(20);
}
int main()
{
test();
return 0;
}
- 上述代码在test函数内申请完内存空间没有进行free释放,该动态内存空间会一直存在于堆区,直至程序结束操作系统主动回收该块空间。虽然程序可以正常运行,但在程序结束前,该空间被浪费