一.什么是动态内存
动态内存区分于静态内存,理论上就是大小可以动态变化的内存存储方式。
静态内存空间开辟的大小是固定的,这会导致内存中只能存放指定的大小,不能调整。
那么动态空间分配的出现,使得程序员可以自行更改内存的大小,让程序更加灵活和方便。
二.动态内存分配使用的函数
注:以下所有函数都包含在<stdlib.h>中。
1.malloc
void* malloc (size_t size);
该函数向内存申请一块size大小的内存,然后返回指向该内存的指针。
注意:
由于malloc只申请空间而不关注类型,所以使用void*的类型。但是如果我们已知欲开辟内存的类型,可以使用强制类型转换来指定相应类型的指针。
若开辟成功,则返回指向该内存的指针;
若开辟失败,则返回空指针。
2.calloc
void* calloc (size_t num, size_t size);
calloc与malloc的唯一区别在于前者会将开辟空间中的内容全部初始化为0。
3.realloc
void* realloc (void* ptr, size_t size);
该函数可以对过去已经申请过的内存空间的大小进行变动。
• ptr 是要调整的内存地址
• size 调整之后新大小
注意:
使用该函数的时候针对对象内存空间会出现两种情况:
a.开辟的新空间同其他已有空间冲突,则会重新开辟一整块的新空间,将原来空间中的内容复制过来,并且释放原有空间,再返回新的起始地址。
可以根据下图理解:
b.开辟的新空间并不冲突,那么直接根据需要扩大的空间大小差值扩大,返回旧的起始地址
然而当要调整的地址是空指针NULL时,那么realloc的作用就与malloc相同。
4.free
void free (void* ptr);
申请了的空间,如果不再需要用到的话,就需要对其进行释放操作,否则会导致内存泄漏,以至于最后申请空间的位置变成一个未知。
• 如果参数 ptr 指向的空间不是动态开辟的(指静态空间),那free函数的行为是未定义的。
• 如果参数 ptr 是NULL指针,则函数什么事都不做。
该函数作为以上三种函数的基础,起着很重要的作用。
三.动态内存分配的意义
1. 灵活性:动态内存分配允许程序在运行时根据需要动态地分配和释放内存空间,从而灵活地管理内存资源,提高内存利用率。
2. 避免静态内存限制:静态内存分配在编译时确定内存大小,可能会受到固定内存大小的限制,而动态内存分配可以根据程序运行时的实际需要动态地分配内存,避免了静态内存大小限制的问题。
3. 内存共享和重用:动态内存分配可以实现内存的共享和重用,提高内存的利用效率,减少内存碎片化。
4. 数据结构的灵活性:动态内存分配可以为数据结构提供灵活的内存管理方式,可以根据数据结构的实际需求动态地分配内存,从而提高数据结构的效率和性能。
四.常见错误
1.申请的内存为NULL,并且对其进行解引用操作,即未判读是否为空指针
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.多次释放
void doubleFreeExample() { int *ptr = (int*)malloc(sizeof(int)); free(ptr); free(ptr); // 尝试释放已经被释放的内存空间 }
4.内存泄漏
void memoryLeakExample() { int *ptr = (int*)malloc(sizeof(int)); // 没有释放分配的内存空间 }
5.释放非动态内存空间
int main() { int a = 10; int *p = &a; free(p);//释放的是非动态内存空间(未使其动态起来) }
6.仅仅释放一部分动态内存(不完整释放)
void test() { int *p = (int *)malloc(100); p++; free(p);//p不再指向动态内存的起始位置,而是指向p++的位置 }