为什么需要动态分配内存?
普通的内存开辟方法自能开辟固定的空间大小,一旦开辟空间的大小就不能改变。这种情况是经常遇到的:需要开辟的空间大小在程序运行时才能确定,这时候提前开辟多大的空间都感觉不是很合适,小了怕不够,大了又担心空间浪费,这时候动态开辟内存就能很好的解决问题。
动态内存管理的相关函数
动态内存管理的相关函数都包含在stdlib.h和malloc.h两个头文件中,使用时引用其中一个即可。
malloc(开辟空间)
void* mallloc(size_t size)
可以看出:malloc向内存申请一块连续可用的空间,并返回指向这块空间的指针。
1.如果开辟成功,则返回一个指向开辟好的空间的指针。
2.如果开辟失败,则返回一个NULL指针,因此malloc的返回值一定要做检查。
int* p = NULL;
p = (int*)malloc(num * sizeof(int))
if(p != NULL){ //检查malloc的返回值
//使用空间
}
3.malloc的返回类型是void*,所以malloc函数开辟空间时并不知道开辟空间的类型。
4.如果参数size为0,malloc的行为是未定义的,取决于编译器(也就是说布置到函数会干什么,不同编译器有不同的做法)
free(释放空间)
void free(void* ptr);
1.如果参数ptr指向的空间不是动态开辟的,则free函数的行为是未定义的。
2.如果参数ptr是NULL空指针,则free函数不做任何操作。
calloc(开辟并初始化空间)
void* calloc(size_t num, size_t size)
calloc与malloc的唯一区别在于calloc在返回指向所开辟空间前会把开辟的所有字节初始化为0 。calloc同样需要检查返回的指针是否为空。
realloc(对开辟的动态空间扩容)
void* realloc(void* ptr, size_t size)
1.ptr是要调整的动态开辟的内存地址。
2.size为调整后的空间大小。
relloc函数的实现值得注意:
1.第一种:待扩展的空间后面有足够的空间以供扩展
2.第二种:待扩展的空间后面有没有足够的空间以供扩展,函数会自动寻找一块足够大的空间重新开辟整个空间,而不是扩展。如果是第二种情况,容易让编程者出现一个错误:relloc扩容后空间对应的指针已经不是原来的指针。对旧的指针进行操作不会对目标空间造成任何改变。
动态管理内存容易发生的错误
1.不检查malloc、calloc、realloc函数的返回值是否为NULL。
2.访问动态开辟的内存之外的空间。
int i = 0;
int* p;
p = malloc(sizeof(int) * 10);//这里动态开辟了可以容纳10个整型元素的空间
for (i = 0; i <= 10; i++);//0~10有11个数,访问到第11个数的时候会发生越界访问
{
p[i] = i;
}
3.向free函数传递一个非动态内存管理函数返回的指针。
4.在动态内存释放后再访问它。
什么是内存泄漏?
内存泄漏:当开辟的动态内存不再需要使用时,他应该被释放,这样这块空间以后才能重新分配使用。而如果只开辟而不释放就会引起内存泄漏。通俗点说,如果不使用free释放动态分配的内存,那么那块内存将不再能被使用。
内存泄漏很难被发现,因为少量的内存泄漏不会造成很大的影响,系统也不会报错。持续的内存泄漏会导致内存一点点的被占用,足够长的时间后,可以使用的内存越来越少,最后系统或者服务器便会因没有内存使用而崩溃。