(c语言)动态内存管理

1.栈区,堆区

栈区

  1. 概念栈区用于存放局部变量,形参等,栈区由系统自动分配内存,当离开作用域自动释放。
  2. 分配大小栈区由高地址向低地址扩展,是连续的内存区域,空间大小一般是2M,能从栈申请的空间比较小。
  3. 分配速度栈区申请内存的速度比堆区要快,当数据量比较大时有明显区别。

堆区 

  1. 概念堆区用于存放通过malloc,realloc,new主动申请的空间,并可以使用free来释放。
  2. 分配大小堆区由低地址向高地址拓展,是不连续的内存区域,堆区可申请的空间更大

如果栈区和堆区都是一个方向拓展的,当栈区或者堆区的数据量过大,栈区的数据就会有放到堆区的问题。所以栈区是由高地址向低地址拓展的,堆区是由低地址向高地址拓展的。

2.为什么需要动态内存分配?

int a = 10  //在栈上开辟4个字节的空间

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

创建整形变量和整形数组时开辟的空间是固定的,有时候我们需要的空间大小在程序运行的时候才能知道,使用动态内存管理可以灵活分配内存

3.如何动态内存管理?

malloc函数

malloc函数向内存申请一块size大小的连续可用空间,并返回指向这片空间的指针

int* ptr = (int*)malloc(sizeof(int));   //动态内存开辟的4个字节空间

  • 如果开辟成功,则返回一个指向开辟好空间的指针
  • 如果开辟失败,则返回一个NULL指针,malloc的返回值需要检查
  • 返回类型是void* ,所以malloc函数使用时要自己决定类型
  • 如果参数size是0,malloc的行为是未定义的,取决于编译器
free函数

free函数是专门用于做动态内存的释放和回收,用于回收malloc,realloc,calloc函数申请的空间

int* ptr = (int*)malloc(sizeof(int));

free(ptr)         //free函数回收malloc申请的空间

ptr = NULL    //指针需要制空,避免变成野指针

  • 如果参数ptr指向的空间不是动态开辟的,那free函数的行为是未定义的
  • 如果参数ptr为NULL,那么函数什么都不做
calloc函数 

 calloc函数为num个大小为size的元素开辟一块空间,并把空间的每一个字节初始化为0

int* ptr = (int*)calloc(sizeof(int));  //动态开辟了4个字节的空间,并且全部初始化为0

  • calloc函数和malloc函数的区别:calloc相当于把malloc申请的空间每一个字节初始化为0
  • 如果我们需要对申请的内容初始化,可以使用calloc函数
realloc函数 

 如果想要将申请的空间增加或者缩小为size大小,可以使用realloc函数做到动态内存大小的调整

int* ptr = (int*)malloc(sizeof(int));         //动态内存开辟的4个字节空间

int* p = (int*)realloc(ptr,2*sizeof(int));  //扩容以后动态内存变成8个字节

  • realloc函数的返回值是调整之后内存的地址
  • realloc函数扩容的空间比较大,后面的空间不足时,realloc函数会寻找一块新的空间并将原来内存的数据转移到新的空间,最后返回新空间的地址

  • realloc函数扩容的空间比较小,后面的空间足够时,realloc函数会在原空间内存后面追加空间,原先的空间数据不变

 4.常见的动态内存错误

使用malloc等函数没有检查返回值

void test()

{

    int* p = (int*)maollc(sizeof(int));
    *p = 4;          //没有检查p指针是否为空

    */正确的写法

     if(p==NULL)

      {

           perror("malloc");

           exit(-1);

      } */ 

    free(p);

    p = NULL;   //释放空间后,需要把p置空
}

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

void test()

{

    int* p = (int*)maollc(10*sizeof(int));

     if(p==NULL)

      {

           perror("malloc");

           exit(-1);

      } 

      for(int i=0;i<=10;i++)

      {

           p[i] = i;   //当i=10时越界访问

      }

    free(p);

    p = NULL;   //释放空间后,需要把p置空
}

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

void test()

{

    int a =10;

    int* p = &a;

    free(p);

}

 使用free释放一部分空间

void test()

{

    int* p = (int*)malloc(sizeof(int));

    p++;

    free(p);
}

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

void test()

{   

    int* p = (int*)malloc(sizeof(int));

    free(p);

    free(p);    //重复释放
}

动态内存忘记释放 

void test()

{

    int* p = (int*)maollc(sizeof(int));

     if(p==NULL)

      {

           perror("malloc");

           exit(-1);

      } 

    *p = 4; 
}

int main()

{

    test();
}

5.柔性数组 

什么是柔性数组?
  • 结构中柔性数组成员前面必须有至少一个成员
  • sizeof返回的结构体的大小不包括柔性数组的内存
  • 包括柔性数组成员的结构用malloc()函数进行内存的分配,并且分配的内存应该大于结构的大小

typedef struct st
{

    int i;

    int a[];

    //不使用柔性数组

    int* a;

    //

}st;

int main()

{

    int j = 0;

    st* p = (st*)malloc(sizeof(st)+40*sizoef(int));

    //不使用柔性数组

    st* p = (st*)malloc(sizeof(st));

    p->a = (int*)malloc(sizeof(int)*40);

    //

    p->i = 40;

    for(j=0;j<40;j++)

    {

        p->a[j] = j;

    }

    free(p)

    return 0;

}

柔数数组优点 
  • 方便内存释放

如果我们在程序中二次进行内存分配,我们释放结构体指针p指向的空间,但是释放内存时可能会忘记释放结构体成员p->a的空间,使用柔性数组直接释放p指向的空间即可

  • 有利于增加访问速度

连续的内存有利于增加访问速度,也有益于减少内存碎片

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值