3.5 对同一块动态内存多次释放
1. #include <stdio.h> 2. #include <string.h> 3. int main() 4. { 5. int* p = (int*)malloc(100); 6. free(p); 7. free(p);//重复释放 8. return 0; 9. }
不可以重复释放(当把p赋值为空指针后再释放是可以的)
3.6 动态开辟内存忘记释放(内存泄漏)
1. #include <stdio.h> 2. #include <stdlib.h> 3. 4. 5. void test() 6. { 7. int* p = (int*)malloc(100); 8. if (NULL != p) 9. { 10. *p = 20; 11. } 12. //记得要释放 13. } 14. int main() 15. { 16. test(); 17. //在这里释放不可以 18. return 0; 19. } 20. 21. //或者把函数返回值改为int*,在主函数接收一下,这样就可以在主函数释放 22. //总而言之,要释放,这块内存要释放
忘记释放不再使用的动态开辟的空间会造成内存泄漏。
记得释放,也要正确释放。
四 经典题目
(1)
1. #include <stdio.h> 2. #include <stdlib.h> 3. 4. void GetMemory(char* p) 5. { 6. p = (char*)malloc(100); 7. //没有释放 8. //并没有返回任何值,也没有改变str的值 9. 10. } 11. 12. void Test(void) 13. { 14. char* str = NULL; 15. GetMemory(str); 16. strcpy(str, "hello world"); 17. //常量字符串传递的是h的地址,所以是正确的 18. //str是空指针,在这里非法访问,程序会崩溃 19. printf(str);//这个写法是可以的,但是程序崩溃无法运行到这里 20. } 21. 22. int main() 23. 24. { 25. Test(); 26. return 0; 27. }
运行结果:程序崩溃
(2)
1. #include <stdio.h> 2. 3. char* GetMemory(void) 4. { 5. char p[] = "hello world"; 6. return p; 7. } 8. //出了这个子函数,地址所指向空间被回收 9. void Test(void) 10. { 11. char* str = NULL; 12. str = GetMemory();//没有这块地址的使用权,所以地址里内容不确定 13. printf(str); 14. } 15. 16. int main() 17. { 18. Test(); 19. return 0; 20. }
(3)
1. #include <stdio.h> 2. #include <stdlib.h> 3. 4. void GetMemory(char** p, int num) 5. { 6. *p = (char*)malloc(num); 7. //没有释放 8. } 9. void Test(void) 10. { 11. char* str = NULL; 12. GetMemory(&str, 100); 13. strcpy(str, "hello"); 14. printf(str); 15. } 16. 17. int main() 18. { 19. Test(); 20. return 0; 21. }
(4)
1. #include <stdio.h> 2. #include <stdlib.h> 3. 4. void Test(void) 5. { 6. char* str = (char*)malloc(100); 7. strcpy(str, "hello"); 8. free(str);//空间释放后,要赋值为空指针 9. if (str != NULL) 10. { 11. strcpy(str, "world");//非法访问(空间被释放,还给操作系统,没有使用的权利,如果使用,就是非法访问) 12. printf(str); 13. } 14. } 15. 16. int main() 17. { 18. Test(); 19. return 0; 20. }
五 C/C++程序的内存开辟
1. 栈区( stack ):在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结
束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是
分配的内存容量有限。 栈区主要存放运行函数而分配的局部变量、函数参数、返回数据、返
回地址等。
2. 堆区( heap ):一般由程序员分配释放, 若程序员不释放,程序结束时可能由 OS 回收 。分
配方式类似于链表。
3. 数据段(静态区)( static )存放全局变量、静态数据。程序结束后由系统释放。
4. 代码段:存放函数体(类成员函数和全局函数)的二进制代码。
实际上普通的局部变量是在 栈区 分配空间的,栈区的特点是在上面创建的变量出了作用域就销毁。但是被 static 修饰的变量存放在 数据段(静态区) ,数据段的特点是在上面创建的变量,直到程序 结束才销毁 所以生命周期变长。
六 柔性数组
C99里,结构中的最后一个元素允许是未知大小的数组,这就叫做『柔性数组』成员。
写法一:
1. struct S1 2. { 3. int n; 4. int arr[0];//大小是未指定的,并不是0 5. };
写法二
1. struct S2 2. { 3. int n; 4. int arr[]; 5. };
6.1 柔性数组的特点
(1)结构中的柔性数组成员前面必须至少一个其他成员。
(2)sizeof 返回的这种结构大小不包括柔性数组的内存。
(3)包含柔性数组成员的结构用 malloc () 函数进行内存的动态分配,并且分配的内存应该大于结构的大 小,以适应柔性数组的预期大小。
代码一:
1. #include <stdio.h> 2. #include <stdlib.h> 3. 4. struct S2 5. { 6. int n; 7. int arr[]; 8. }; 9. 10. int main() 11. { 12. struct S2* ps = (struct S2*)malloc(sizeof(struct S2) + 40); 13. ps->n = 100; 14. int i = 0; 15. for (i = 0; i < 10; i++) 16. { 17. ps->arr[i] = i; 18. } 19. //增容 20. struct S2* ptr = (struct S2*)realloc(ps, sizeof(struct S2) + 80); 21. if (ptr == NULL) 22. { 23. return 0; 24. } 25. else 26. { 27. ps = ptr; 28. } 29. 30. free(ps); 31. ps = NULL; 32. 33. return 0; 34. }
6.2 柔性数组的优势
代码二:
1. #include <stdio.h> 2. #include <stdlib.h> 3. 4. struct S2 5. { 6. int n; 7. int* arr; 8. }; 9. 10. int main() 11. { 12. struct S2* ps = (struct S2*)malloc(sizeof(struct S2)); 13. ps->n = 100; 14. ps->arr = (int*)molloc(40); 15. //增容 16. free(ps->arr); 17. ps->arr = NULL; 18. free(ps); 19. ps = NULL; 20. 21. free(ps); 22. ps = NULL; 23. 24. return 0; 25. }
代码一相对于来说比较好:(1)方便内存释放 如果我们的代码是在一个给别人用的函数中,你在里面做了二次内存分配,并把整个结构体返回给 用户。用户调用free可以释放结构体,但是用户并不知道这个结构体内的成员也需要free,所以你 不能指望用户来发现这个事。所以,如果我们把结构体的内存以及其成员要的内存一次性分配好 了,并返回给用户一个结构体指针,用户做一次free就可以把所有的内存也给释放掉。 (2)这样有利于访问速度. 连续的内存有益于提高访问速度,也有益于减少内存碎片。
(代码二:开辟和释放的次数多,容易出错;容易形成内存碎片)