• C【动态内存管理】


    1. 为什么存在动态内存分配

    1. int val = 20;//在栈空间上开辟四个字节
    2. char arr[10] = {0};//在栈空间上开辟10个字节的连续空间

    2. 动态内存函数的介绍

    2.1 malloc:stdlib.h

    1. void* malloc (size_t size);
    2. int* p = (int*)malloc(40);

    1. #include
    2. #include
    3. #include
    4. #include
    5. int main()
    6. {
    7. //向内存申请10个整形的空间
    8. int* p = (int*)malloc(40);
    9. if (p == NULL)
    10. {
    11. //打印错误原因的一个方式
    12. printf("%s\n", strerror(errno));
    13. }
    14. else
    15. {
    16. //正常使用空间
    17. int i = 0;
    18. for (i = 0; i < 10; i++)
    19. {
    20. *(p + i) = i;
    21. }
    22. for (i = 0; i < 10; i++)
    23. {
    24. printf("%d ", *(p + i));
    25. }
    26. }
    27. //当动态申请的空间不再使用的时候
    28. //就应该还给操作系统
    29. free(p);
    30. //上面是将p断开,但是实际上p还是存储内容,所以我们手动设置为null
    31. p = NULL;
    32. return 0;
    33. }

    2.2 free:stdlib.h

    是用来做动态内存的释放和回收的

    注意点:free(str)后,实际上str还执行一个空的地址,所以此时str!=NULL

    void free (void* ptr);

    2.3 calloc

    calloc 函数也用来动态内存分配。可以初始化空间。

    1. void* calloc (size_t num, size_t size);
    2. int*p = (int*)calloc(10, sizeof(int));
    1. int main()
    2. {
    3. //malloc(10*sizeof(int))
    4. int*p = (int*)calloc(10, sizeof(int));
    5. if (p == NULL)
    6. {
    7. printf("%s\n", strerror(errno));
    8. }
    9. else
    10. {
    11. int i = 0;
    12. for (i = 0; i < 10; i++)
    13. {
    14. printf("%d ", *(p + i));
    15. }
    16. }
    17. //释放空间
    18. //free函数是用来释放动态开辟的空间的
    19. free(p);
    20. p = NULL;
    21. return 0;
    22. }

    2.4 realloc

    当初始申请空间不够,这时使用realloc开辟新的空间【调整动态开辟内存空间的大小】

    使用注意点:

    1. 如果p指向的空间之后有足够的内存空间可以追加,则直接追加,后返回p
    2. 如果p指向的空间之后没有足够的内存空间可以追加,则realloc函数会重新找一个新的内存区域
    开辟一块满足需求的空间,并且把原来内存中的数据拷贝回来,释放旧的内存空间
    最后返回新开辟的内存空间地址
    3. 得用一个新的变量来接受realloc函数的返回值

    void* realloc (void* ptr, size_t size);

    1. #include
    2. int main()
    3. {
    4. int *ptr = (int*)malloc(100);
    5. if(ptr != NULL)
    6. {
    7. //业务处理
    8. }
    9. else
    10. {
    11. exit(EXIT_FAILURE);
    12. }
    13. //扩展容量
    14. //代码1
    15. ptr = (int*)realloc(ptr, 1000);//这样可以吗?(如果申请失败会如何?)
    16. //代码2
    17. int*p = NULL;
    18. p = realloc(ptr, 1000);
    19. if(p != NULL)
    20. {
    21. ptr = p;
    22. }
    23. //业务处理
    24. free(ptr);
    25. return 0;
    26. }

    3. 常见的动态内存错误

    3.1 对NULL指针的解引用操作

    1. //1. 对NULL进行解引用操作
    2. int *p = (int*)malloc(40);
    3. //万一malloc失败了,p就被赋值为NULL
    4. //所以我们在申请完一块空间之后,一定要进行判空操作
    5. *p = 0;//err
    6. int i = 0;
    7. for (i = 0; i < 10; i++)
    8. {
    9. *(p + i) = i;//err
    10. }
    11. free(p);
    12. p = NULL;

    3.2 对动态开辟空间的越界访问

    1. //2. 对动态开辟的内存的越界访问
    2. int *p = (int*)malloc(5 * sizeof(int));
    3. if (p == NULL)
    4. {
    5. return 0;
    6. }
    7. else
    8. {
    9. int i = 0;
    10. for (i = 0; i < 10; i++)
    11. {
    12. *(p + i) = i;
    13. }
    14. }
    15. //
    16. free(p);
    17. p = NULL;

    3.3 对非动态开辟内存使用free释放

    1. //栈区开辟出来的,不是动态开辟
    2. int a = 10;
    3. int* p = &a;
    4. *p = 20;
    5. //3. 对非动态开辟内存的free
    6. free(p);
    7. p = NULL;
    8. return 0;

    3.4 使用free释放一块动态开辟内存的一部分

    1. int*p = (int*)malloc(40);
    2. if (p == NULL)
    3. {
    4. return 0;
    5. }
    6. int i = 0;
    7. for (i = 0; i < 5; i++)
    8. {
    9. *p++ = i;
    10. }
    11. //回收空间
    12. // 使用free释放动态开辟内存的一部分
    13. free(p);
    14. p =NULL;

    3.5 对同一块动态内存多次释放

    1. int *p = (int*)malloc(40);
    2. if (p == NULL)
    3. {
    4. return 0;
    5. }
    6. //使用
    7. //释放
    8. free(p);
    9. //将p设置为空指针,可以防止重复释放产生的错误
    10. p = NULL;

    3.6 动态开辟内存忘记释放(内存泄漏)

    1. while (1)
    2. {
    3. malloc(1);
    4. }

    4. 几个经典的笔试题

    4.1 题目1:

    1. //面试1:
    2. void GetMemory(char *p)
    3. {
    4. p = (char *)malloc(100);
    5. }
    6. void Test(void)
    7. {
    8. char *str = NULL;
    9. GetMemory(str);
    10. strcpy(str, "hello world");
    11. printf(str);
    12. }
    13. int main()
    14. {
    15. Test();
    16. char*str = "abcdef";
    17. printf("%s\n", str);
    18. printf(str);
    19. printf("abcdef");
    20. return 0;
    21. }

    修改结果

    1. void GetMemory(char **p)//**p:是p的地址
    2. {
    3. //*p:是p的内容
    4. *p = (char *)malloc(100);
    5. }
    6. void Test(void)
    7. {
    8. char *str = NULL;
    9. GetMemory(&str);//传地址
    10. strcpy(str, "hello world");
    11. printf(str);
    12. free(str);
    13. str = NULL;
    14. }
    15. int main()
    16. {
    17. Test();
    18. return 0;
    19. }
    1. char* GetMemory(char *p)
    2. {
    3. p = (char *)malloc(100);
    4. //将p传递除去
    5. return p;
    6. }
    7. void Test(void)
    8. {
    9. char *str = NULL;
    10. str = GetMemory(str);
    11. strcpy(str, "hello world");
    12. printf(str);
    13. free(str);
    14. str = NULL;
    15. }
    16. int main()
    17. {
    18. Test();
    19. return 0;
    20. }

    4.2 题目2:

    ​​​​​​​

    【存储在栈区中的数据,出了函数则就会被销毁】

    1. //面试2
    2. char *GetMemory(void)
    3. {
    4. char p[] = "hello world";//局部变量
    5. //跳出此函数,则p被销毁
    6. return p;
    7. }
    8. void Test(void)
    9. {
    10. char *str = NULL;
    11. str = GetMemory();//此时str的p的地址
    12. printf(str);//非法地址访问,故输出随机值
    13. }
    14. int main()
    15. {
    16. Test();
    17. return 0;
    18. }

    【数据存储在静态区(static),出了函数数据还是存在】

    1. //在静态区,出了函数还是可以继续使用
    2. int* test()
    3. {
    4. //使用static,将a放入静态区,出了这个函数,内存并没有被销毁,故在外面还可以访问到
    5. static int a = 10;//静态区
    6. int a = 10;//栈区
    7. return &a;
    8. }
    9. int main()
    10. {
    11. int*p = test();//此时p接收到a的地址
    12. *p = 20;//将a修改为20
    13. return 0;//20
    14. }

    【数据存储在堆区,出了函数数据还是存在】

    1. //在堆区,出函数还是存在
    2. int* test()
    3. {
    4. int *ptr = malloc(100);//堆区
    5. return ptr;
    6. }
    7. int main()
    8. {
    9. int *p = test();
    10. return 0;
    11. }

    4.3 题目3:

    1. void GetMemory(char **p, int num)
    2. {
    3. *p = (char *)malloc(num);//给p创建100个新的char
    4. }
    5. void Test(void)
    6. {
    7. char *str = NULL;
    8. GetMemory(&str, 100);
    9. strcpy(str, "hello");//可以输出
    10. printf(str);
    11. //改:忘记free内容,导致内存泄露
    12. free(str);
    13. str = NULL;
    14. }

    4.4 题目4:

    1. void Test(void)
    2. {
    3. char *str = (char *)malloc(100);
    4. strcpy(str, "hello");
    5. free(str);
    6. //free了但是没有把指针置为null
    7. //此处的问题:已经释放的空间,还被使用
    8. if (str != NULL)//则此时str还不为NUll,则进入判断
    9. {
    10. strcpy(str, "world");
    11. printf(str);
    12. }
    13. }
    14. int main()
    15. {
    16. Test();//world
    17. return 0;
    18. }

    解决:

    1. void Test(void)
    2. {
    3. char* str = (char*)malloc(100);
    4. strcpy(str, "hello");
    5. free(str);
    6. //解决:将str置为NULL
    7. str = NULL;
    8. if (str != NULL)
    9. {
    10. strcpy(str, "world");
    11. printf(str);
    12. }
    13. }
    14. int main()
    15. {
    16. Test();//world
    17. return 0;
    18. }

    5. C/C++程序的内存开辟

    C/C++程序内存分配的几个区域:

    1. 栈区(stack):在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结 束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是 分配的内存容量有限。 栈区主要存放运行函数而分配的局部变量、函数参数、返回数据、返 回地址等。

    2. 堆区(heap):一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收 。分 配方式类似于链表。

    3. 数据段(静态区)(static)存放全局变量、静态数据。程序结束后由系统释放。

    4. 代码段:存放函数体(类成员函数和全局函数)的二进制代码。

    6. 柔性数组

    结构中的最后一个元素允许是未知大小的数组,这就叫做『柔性数组』成员。

    1. struct S
    2. {
    3. int n;
    4. int arr[10];
    5. };
    6. struct S
    7. {
    8. int n;
    9. int arr[];//未知大小的
    10. };
    11. struct S
    12. {
    13. int n;
    14. int arr[0];//未知大小的-柔性数组成员-数组的大小是可以调整的
    15. };

    6.1 柔性数组的特点:

    • 结构中的柔性数组成员前面必须至少一个其他成员。
    • sizeof 返回的这种结构大小不包括柔性数组的内存。
    • 包含柔性数组成员的结构用malloc ()函数进行内存的动态分配,并且分配的内存应该大于结构的大 小,以适应柔性数组的预期大小。
    1. //代码1
    2. int i = 0;
    3. type_a *p = (type_a*)malloc(sizeof(type_a)+100*sizeof(int));
    4. //业务处理
    5. p->i = 100;
    6. for(i=0; i<100; i++)
    7. {
    8. p->a[i] = i;
    9. }
    10. free(p);

    6.2 柔性数组的使用--int[] a

    1. struct S
    2. {
    3. int n;
    4. int arr[0];//未知大小的-柔性数组成员-数组的大小是可以调整的
    5. };
    6. int main()
    7. {
    8. struct S s;
    9. printf("%d\n", sizeof(s));//
    10. //sizeof(struct S):不包括int arr的大小
    11. //5*sizeof(int):手动的给arr赋值
    12. struct S* ps = (struct S*)malloc(sizeof(struct S)+5*sizeof(int));
    13. ps->n = 100;
    14. int i = 0;
    15. for (i = 0; i < 5; i++)
    16. {
    17. ps->arr[i] = i;//0 1 2 3 4
    18. }
    19. //开辟内存
    20. struct S* ptr = realloc(ps, 44);
    21. if (ptr != NULL)
    22. {
    23. ps = ptr;
    24. }
    25. for (i = 5; i < 10; i++)
    26. {
    27. ps->arr[i] = i;
    28. }
    29. //打印arr所有数值
    30. for (i = 0; i < 10; i++)
    31. {
    32. printf("%d ", ps->arr[i]);
    33. }
    34. //释放
    35. free(ps);
    36. ps = NULL;
    37. return 0;
    38. }

    6.3 柔性数组的扩展:int* arr

    1. struct S
    2. {
    3. int n;
    4. int* arr;
    5. };
    6. int main()
    7. {
    8. //sizeof(struct S):此时包括int* arr
    9. struct S* ps = (struct S*)malloc(sizeof(struct S));
    10. //再一次给arr创建动态内存
    11. ps->arr = malloc(5 * sizeof(int));
    12. int i = 0;
    13. for (i = 0; i < 5; i++)
    14. {
    15. ps->arr[i] = i;
    16. }
    17. for (i = 0; i < 5; i++)
    18. {
    19. printf("%d ", ps->arr[i]);
    20. }
    21. //调整大小
    22. int* ptr = realloc(ps->arr, 10 * sizeof(int));
    23. if (ptr != NULL)
    24. {
    25. ps->arr = ptr;
    26. }
    27. for (i = 5; i < 10; i++)
    28. {
    29. ps->arr[i] = i;
    30. }
    31. for (i = 0; i < 10; i++)
    32. {
    33. printf("%d ", ps->arr[i]);
    34. }
    35. //释放内存:注意释放顺序
    36. free(ps->arr);
    37. ps->arr = NULL;
    38. free(ps);
    39. ps = NULL;
    40. return 0;
    41. }

    6.4 int arr[0] 和 int* arr的区别

    上述 代码1 和 代码2 可以完成同样的功能,但是 方法1 的实现有两个好处:

    ​​​​​​​

  • 相关阅读:
    手写数字识别-基于卷积神经网络
    【SpringBoot】SpringBoot开启MyBatis缓存+ehcache(一二级缓存和myBatis的差不多,第三方缓存是jar包的不一样)
    tomcat启动,测试被拒绝连接
    简单的个人博客网站设计 静态HTML个人博客主页 DW个人网站模板下载 大学生简单个人网页作品代码 个人网页制作 学生个人网页设计作业
    Unity UGUI(一)基础组件
    力扣-338.比特位计数
    web技术——HTML文档基础部分(1)
    1212. 查询球队积分
    Python——爬取歌单(含源代码)
    添加docker容器数据卷
  • 原文地址:https://blog.csdn.net/m0_63077733/article/details/132914828