重点
(1)为什么存在动态内存分配 (2)动态内存函数的介绍 molloc free calloc realloc (3)常见的动态内存错误(4)题目(5)柔型数组
一 为什么存在动态内存分配
int a = 4; int arr[40] = { 0 };
上面的内存开辟(1)空间开辟大小是固定的(2)数组在申明的时候,必须指定数组的长度,它所需要的内存在编译时分配。
但是,对空间的需求,不仅仅是上述的情况,需要的空间大小在运行的时候才能知道,可能有的空间比较多,有的需要的空间不足,这个时候就需要动态内存分配了。
动态内存分配在堆区
二 动态内存函数的介绍
在malloc、calloc、realloc创建的空间需要释放
2.1 malloc和free
这两个函数的头文件是<stdlib.h>
malloc
这是一个动态内存开辟的函数
void* malloc ( size_t size );
size_t size 单位是字节,因为不知道开辟的空间是什么类型的,所以直接用字节数来表示
这个函数向内存申请一块 连续可用 的空间,并返回指向这块空间的指针。
(1)如果开辟成功,则返回一个指向开辟好空间的指针。
(2)如果开辟失败,则返回一个 NULL 指针,因此 malloc 的返回值一定要做检查。
(3)返回值的类型是 void* ,所以 malloc 函数并不知道开辟空间的类型,具体在使用的时候使用者自己 来决定。
(4)如果参数 size 为 0 , malloc 的行为是标准是未定义的,取决于编译器。
free
这是一个动态内存的开辟和回收的函数
void free ( void* ptr );
free 函数用来释放动态开辟的内存。
(1)如果参数 ptr 指向的空间不是动态开辟的,那 free 函数的行为是未定义的。
(2)如果参数 ptr 是 NULL 指针,则函数什么事都不做。
1. #include <stdio.h> 2. #include <stdlib.h> 3. #include <string.h> //strerror函数头文件 4. #include <errno.h> //errno这个变量头文件 5. 6. int main() 7. { 8. //开辟10个整形的空间int arr[10]; 9. int* p = (int*)malloc(40);//malloc返回的是void* 10. //如果开辟失败,返回的是一个空指针,所以要先检查,后使用 11. if (p == NULL) 12. { 13. printf("%s\n", strerror(errno));//字符串 14. return 0; 15. } 16. 17. //使用 18. int i = 0; 19. for (i = 0; i < 10; i++) 20. { 21. *(p + 1) = i; 22. printf("%d ", *(p + 1)); 23. } 24. 25. //释放这份空间,还给操作系统 26. free(p); 27. //但是p还是保存的这个地址,就变成了野指针, 28. p = NULL;//就可以防止野指针这种操作 29. return 0; 30. }
提示:野指针成因之一:指针指向的空间释放(在C语言初阶专栏的初阶指针知识点的文章中有详细写到野指针成因)http://t.csdn.cn/nr6EZ(文章链接)
2.2 calloc
头文件是<stdlib.h>
calloc 函数也用来动态内存分配
void* calloc ( size_t num , size_t size );
num元素个数 size每个元素的大小 例如: calloc(10,sizeof(int))
函数的功能是为 num 个大小为 size 的元素开辟一块空间,并且把空间的每个字节初始化为 0 。
与函数 malloc 的区别只在于 calloc 会在返回地址之前把申请的空间的每个字节初始化为全 0 。
增容失败,返回空指针。
2.3 realloc
头文件是<stdlib.h>
(1)realloc 函数的出现让动态内存管理更加灵活。
(2)有时会我们发现过去申请的空间太小了,有时候我们又会觉得申请的空间过大了,那为了合理的时 候内存,我们一定会对内存的大小做灵活的调整。那 realloc 函数就可以做到对动态开辟内存大小 的调整。
void* realloc ( void* ptr , size_t size );
(1)ptr 是要调整的内存地址
(2)size 调整之后新大小
(3)返回值为调整之后的内存起始位置。
(4)这个函数调整原内存空间大小的基础上,还会将原来内存中的数据移动到 新 的空间。
(1)原有的空间后面的空间足够,要扩展内存就直接原有内存之后直接追加空间,原来空间的数据不发生变化。
(2)原有的空间后面的空间不够,当是情况 2 的时候,原有空间之后没有足够多的空间时,扩展的方法是:在堆空间上另找一个合适大小的连续空间来使用。这样函数返回的是一个新的内存地址。
1. #include <stdlib.h> 2. #include <stdio.h> 3. #include <string.h> //strerror函数头文件 4. #include <errno.h> //errno这个变量头文件 5. 6. int main() 7. { 8. //开辟10个整形的空间int arr[10]; 9. int* p = (int*)malloc(40);//malloc返回的是void* 10. //如果开辟失败,返回的是一个空指针,所以要先检查,后使用 11. if (p == NULL) 12. { 13. printf("%s\n", strerror(errno));//字符串 14. return 0; 15. } 16. //使用 17. int i = 0; 18. for (i = 0; i < 10; i++) 19. { 20. *(p + 1) = i; 21. printf("%d ", *(p + 1)); 22. } 23. //增容 24. int* ptr = (int*)realloc(p, 80); 25. if (ptr != NULL) 26. { 27. p = ptr; 28. ptr = NULL;//因为这个地址,后面会被释放 29. } 30. 31. 32. //释放这份空间,还给操作系统 33. free(p); 34. //但是p还是保存的这个地址,就变成了野指针, 35. //野指针成因之一:指针指向的空间释放(在C语言初阶专栏的初阶指针知识点的文章中有详细写到野指针成因) 36. p = NULL; 37. return 0; 38. }
这个函数也会出现增容失败的时候。(返回空指针,意思空间被释放,会导致原来的空间也被释放)(所以不能把返回的指针直接放在原来的指针变量里,应该再定义一个指针变量存放返回地址,先判断是否是空指针(增容失败),不是空指针,再赋值给p)
realloc(NULL,40),就和malloc一样了
三 常见的动态内存错误
3.1 对NULL指针的解引用
1. void test() 2. { 3. int *p = (int *)malloc(INT_MAX/4); 4. *p = 20;//如果p的值是NULL,就会有问题 5. free(p); 6. }
应该先判断是否为空指针
3.2 对动态开辟空间的越界访问
1. #include <stdio.h> 2. #include <stdlib.h> 3. #include <string.h> 4. #include <errno.h> 5. 6. 7. int main() 8. { 9. int i = 0; 10. int* p = (int*)malloc(10 * sizeof(int)); 11. if (NULL == p) 12. { 13. printf("%s\n", strerror(errno)); 14. } 15. for (i = 0; i <= 10; i++) 16. { 17. *(p + i) = i;//当i是10的时候越界访问 18. } 19. free(p); 20. p = NULL; 21. return 0; 22. }
不可以越界访问
3.3 对非动态开辟内存使用free释放
1. #include <stdio.h> 2. #include <string.h> 3. 4. int main() 5. { 6. int a = 10; 7. int* p = &a; 8. free(p);//非动态开辟,不可以释放 9. return 0; 10. }
3.4 使用free释放一块动态开启内存的一部分
1. #include <stdio.h> 2. #include <string.h> 3. #include <stdlib.h> 4. #include <errno.h> 5. 6. int main() 7. { 8. int* p = (int*)malloc(100); 9. if (p == NULL) 10. { 11. printf("%s\n", strerror(errno)); 12. return 0; 13. } 14. 15. p++; 16. free(p);//p不再指向动态内存的起始位置,不能释放 17. return 0; 18. }