【C语言进阶篇】动态内存管理

【C语言进阶篇】动态内存管理

🌈个人主页:开敲

🔥所属专栏:C语言

🌼文章目录🌼

1. 为什么要有动态内存分配

2.动态内存开辟和释放函数

   2.1 动态内存释放函数

      2.1.1 free函数

   2.2 动态内存开辟函数

      2.2.1 malloc函数 

      2.2.2 calloc函数

      2.2.3 realloc函数

3. 常见的动态内存的错误

    3.1 对NULL指针的解引用操作

    3.2 对动态开辟内存的越界访问

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

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

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

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

4. 动态内存经典笔试题练习

5. 柔性数组

    5.1 柔性数组的特点

    5.2 柔性数组的使用

    5.3 柔性数组的优势

1. 为什么要有动态内存分配

  在我们之前的学习中,掌握的开辟内存的方式有:

1 int a = 0;//在上开辟四个字节的空间

int arr[10] = {0};//在上开辟10个整型(40个字节)的空间

但是上面这种开辟空间的方法有两个特点:

 开辟空间的大小是固定的

② 数组在声明的时候,一旦数组的大小确定下来了,在编译时就不能改变

  但在我们实际的情况中,对于空间的需求不仅仅是上述的情况,实际我们可能在编译的时候才能确定所需空间的大小,这个时候上面这写开辟空间的方式就不适用了。这个时候就需要用到我们灵活(动态)地进行内存地开辟。

  C语言引入了动态内存开辟,这使得程序员能够自己根据实际需求来申请释放空间,使代码更加的灵活。

  下面就来介绍几个动态内存开辟的函数和动态内存释放的函数。

2.动态内存开辟和释放函数

  这些函数都声明在头文件<stdlib.h>

   2.1 动态内存释放函数
      2.1.1 free函数

  free函数是动态内存管理中必不可少的十分重要的一个函数,用来将动态开辟的内存释放和回收,函数说明如下:

 ① 如果参数ptr所指向的空间不是动态开辟的,那么free的行为是未定义的

 ② 如果参数ptr是NULL指针,则函数什么事也不干

  函数使用:

   2.2 动态内存开辟函数
      2.2.1 malloc函数 

  这个函数向内存申请一块连续的可用地址,并且返回这块地址的指针。size(需要开辟的空间大小,单位是字节)下面是关于这个函数的一些说明:

   如果开辟成功,则返回一个指向开辟好的空间地址的指针

  如果开辟失败,则返回一个NULL指针。(因此,malloc的返回值必须进行检查)

  ③ 返回指针的类型为void*,因此malloc可以返回一个任意类型的指针,具体类型由使用者决定

  ④ 如果参数size为0,malloc执行的行为是未定义的,具体由编译器决定

  函数使用:

      2.2.2 calloc函数

  calloc函数与malloc函数十分相似,区别在于calloc函数的参数部分多了一个num(需要开辟的元素个数),size(每个元素的大小)并且calloc函数在开辟空间时会将开辟好的空间里的内容初始化为0:

 

      2.2.3 realloc函数

  在我们动态开辟内存时,时而会发现申请的空间太小了,时而又会发现申请的空间太大了,那么为了能够合理地使用内存,我们就需要对内存的大小进行调整。realloc函数就可以做到对动态开辟内存大小的调整。

  realloc函数的作用是:将动态开辟的空间扩容。

  ① ptr是要调整的内存的地址

  size是调整之后希望的大小

  ③ 返回的是调整之后的内存起始地址

  ④ 在调整好空间后,将原有空间内已经存放的数据拷贝到新的空间中

   ​​​​​​​realloc在调整内存空间大小时有以下两种情况:

    1. 原有空间后有足够大的空间存放扩容后的空间,直接向后开辟新的空间

    2. 原有空间后没有足够大的空间,则重新找一块可用的足够大的空间进行开辟,然后返回新空间的地址,同时将原有空间的数据拷贝到新的空间中

由于上述两种情况的存在,在使用realloc函数时必须对realloc返回的指针进行是否为NULL指针的检查。

3. 常见的动态内存的错误
  3.1 对NULL指针的解引用操作

  1  void text()

  2   {

  3        int* p = (int*)malloc(INT_MAX);//这里由于需要开辟的空间太大,导致无法正常开辟

  4                                                          //空间,malloc返回一个NULL指针

  5        *p = 20;//这里的p为NULL指针,对NULL指针解引用操作

  6         free(p);

  7   }

    3.2 对动态开辟内存的越界访问

  void text()

2       {

3                int i = 0;

              int* pf = (int*)malloc(10*sizeof(int));

              if(pf==NULL)

6                     {

7                           exit(EIXT_FAILURE);

8                     }     

9                 for(i = 0;i<=10;i++)  //循环次数为11次 

10                   {

11                          *(p+i) = i;

12                   }

13                free(pf);

14                pf = NULL;

15       }          

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

1    void text()

2       {

3             int a = 10;

4             int*p = &a;

5             free(p);

6       }

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

1    void text()

2       {

3              int* pf = (int*)malloc(5*sizeof(int));

4              pf++;    //pf不再指向动态开辟内存的起始地址

5              free(p);

6       }

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

1    void text()

2       {

3             int* pf = (int*)malloc(5*sizeof(int));

4             free(p);

5             free(p);

6       }

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

  这是动态内存错误中最严重的一个错误

1    void text()

2        {

3               int* pf = (int*)malloc(5*sizeof(int));

4               if(pf != NULL)

5                     {

6                           *p = 20;

7                      }

8        }

9

10    int main()

11       {

12            text();

13            while(1);//这里再出函数之后直接进入了死循环,函数内也没有释放内存,导致

14                           //开辟的这块空间无法释放,也无法使用,导致内存泄漏

15       }

切记:动态开辟的内存一定要释放,并且正确释放!

4. 动态内存经典笔试题练习

  题目1:

题目2:

题目3:

题目4:

5. 柔性数组

  也许你从来没有听说过柔性数组(flexible array)这个概念,但它确实是存在的。

  C99中,结构体中的最后一个元素是位置大小的数组,这个数组就是柔性数组。例如:

1    typedef struct st_sype

2       {

3              int i = 0;

4              int a[ ];

5       }type_a;

    5.1 柔性数组的特点

   结构体中的柔性数组成员前必须有至少一个其它成员(保证柔性数组是结构体的最后一个成员)

  ② sizeof在计算这种结构体时,不会算入柔性数组的大小

  ③ 包含柔性数组成员的结构用malloc()函数进行内存的动态分配,并且分配的内存应当大于sizeof计算结构体的大小,以保证能够满足柔性数组的预期大小。

  例如:

​​​​​​​1       typedef struct st_type
2           {
3               int i;
4               int a[0];//柔性数组成员
5           }type_a;

6
7       int main()
8           {
9              printf("%d\n", sizeof(type_a));//输出的是4
10              return 0;
11          }
 

    5.2 柔性数组的使用

  1  //代码1
  2   #include <stdio.h>
  3   #include <stdlib.h>
  4     int main()
  5        int i = 0;
  6           type_a *p = (type_a*)malloc(sizeof(type_a)+100*sizeof(int));
  7           //业务处理
  8            p->i = 100;
  9            for(i=0; i<100; i++)
  10               {
  11                    p->a[i] = i;
  12               }
  13           free(p);
  14           return 0;
  15        }
 

这样柔性数组成员a相当于获得了100个整型元素的连续空间。

  上述的type_a结构也可以设计为下面的结构,也能完成相同的效果。

//代码2
2     #include <stdio.h>
3     #include <stdlib.h>

4
5      typedef struct st_type
6             {
7                 int i;
8                 int *p_a;
9             }type_a;
10        int main()

11          {

12             type_a *p = (type_a *)malloc(sizeof(type_a));
13              p->i = 100;
14              p->p_a = (int *)malloc(p->i*sizeof(int));
15              //业务处理
16              for(i=0; i<100; i++)
17                   {
18                        p->p_a[i] = i;
19                   }
20               //释放空间
21               free(p->p_a);
22               p->p_a = NULL;
23               free(p);

24               p = NULL;
25               return 0;
26             }
 

    5.3 柔性数组的优势

  上述代码1代码2可以完成相同的功能,但是代码1有两个好处:

  ①:方便内存释放

  如果我们的代码是在⼀个给别人用的函数中,你在里面做了二次内存分配,并把整个结构体返回给用户。用户调用free可以释放结构体,但是用户并不知道这个结构体内的成员也需要free,所以你不能指望用户来发现这个事。所以,如果我们把结构体的内存以及其成员要的内存⼀次性分配好了,并返回给用户⼀个结构体指针,用户做⼀次free就可以把所有的内存也给释放掉。

  ②:有利于访问速度

  连续的内存有益于提高访问速度以及提高内存的利用率,也有益于减少内存碎片,提高内存的利用率。

                                                  创作不易,点个赞呗,谢谢啦~

  

相关推荐

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-03-24 09:52:04       94 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-03-24 09:52:04       100 阅读
  3. 在Django里面运行非项目文件

    2024-03-24 09:52:04       82 阅读
  4. Python语言-面向对象

    2024-03-24 09:52:04       91 阅读

热门阅读

  1. 模板 前缀和 NC

    2024-03-24 09:52:04       42 阅读
  2. 基于单片机的小区安防控制系统设计

    2024-03-24 09:52:04       38 阅读
  3. 如何在OpenCV中实现实时人脸识别?

    2024-03-24 09:52:04       38 阅读
  4. 24计算机考研调剂 | 江西理工大学

    2024-03-24 09:52:04       41 阅读
  5. 日志收集监控告警平台的选型思考

    2024-03-24 09:52:04       38 阅读
  6. Github 2024-03-24 开源项目日报Top10

    2024-03-24 09:52:04       38 阅读
  7. 数据库第一次作业

    2024-03-24 09:52:04       36 阅读