【C语言】动态内存管理,及柔性数组介绍!有浅到深!

 🔥博客主页🔥:【 坊钰_CSDN博客 】

欢迎各位点赞👍评论✍收藏⭐

 

目录

 

1. 动态内存管理是什么

2. 为什么有动态内存分配

 3. 动态内存分配常用函数

3.1 malloc函数

 3.2 free函数

3.3 calloc函数

3.4 realloc函数

4. 动态内存常见错误

4.1 对空指针(NULL)的解引用

4.2 对开辟的空间造成越界访问

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

4.4 free释放一块动态开辟内存的一部分

4.5 多次使用free释放(同一区域)

4.6 忘记释放(内存泄漏)

5. 柔性数组

5.1 柔性数组特点

 5.2 柔性数组使用

5.3 柔性数组好处

6. 小结


1. 动态内存管理是什么

首先动态内存管理的空间在堆区,是让程序员灵活控制内存的大小;

2. 为什么有动态内存分配

我们了解的开辟方式有:

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

上述有两种特点

  1. 空间开辟大小是固定的
  2. 数组在申明的时候,必须指定数组的⻓度,数组空间⼀旦确定了大小不能调整

有时候我们需要的空间大小在程序运⾏的时候才能知 道,那数组的编译时开辟空间的方式就不能满⾜了 

引⼊了动态内存开辟,让程序员⾃⼰可以申请和释放空间,就⽐较灵活了

 3. 动态内存分配常用函数

3.1 malloc函数

void* malloc (size_t size);

 这个函数向内存申请⼀块连续可⽤的空间,并返回指向这块空间的指针

它的规则如下:

  1. 如果开辟成功,则返回⼀个指向开辟好空间的指针
  2. 如果开辟失败,则返回⼀个 NULL 指针,因此malloc的返回值⼀定要做检查
  3. 返回值的类型是 void* ,所以malloc函数并不知道开辟空间的类型,具体在使⽤的时候使⽤者⾃⼰来决定
  4. 如果参数 size 为0,malloc的⾏为是标准是未定义的,取决于编译器

例:

#include <stdio.h>
#include <stdlib.h>
int main()
{
    // malloc
    int* pf = (int*)malloc(40); // 40字节
    if (pf != NULL)     //一定要检查是否为空指针
    {
        for (int i = 0; i < 10; i++)   // 对于整型则为10个元素
        {
            *(pf + i) = i+1;
        }
    }
    else
    {
        perror("malloc");
    }
    free(pf);
    pf = NULL;
    return 0;
}

 3.2 free函数

void free (void* ptr);

函数free,专⻔是⽤来做动态内存的释放和回收的

free函数⽤来释放动态开辟的内存

  1. 如果参数 ptr 指向的空间不是动态开辟的,那free函数的⾏为是未定义的
  2. 如果参数 ptr 是NULL指针,则函数什么事都不做

注意:free函数释放后,要把指针设为空指针(防止成为野指针) ;

例:

#include <stdio.h>
#include <stdlib.h>
int main()
{
    int num = 0;
    scanf("%d", &num);
    int arr[num] = { 0 };
    int* ptr = NULL;
    ptr = (int*)malloc(num * sizeof(int));

    if(NULL != ptr)//判断ptr指针是否为空 
    {
        int i = 0;
        for (i = 0; i < num; i++)
        {
            *(ptr + i) = 0;
        }
    }
    free(ptr);    //释放ptr所指向的动态内存 
    ptr = NULL;   //防止成为野指针 
    return 0;
}

3.3 calloc函数

void* calloc (size_t num, size_t size);

calloc 函数也⽤来动态内存分配

  1. 函数的功能是为 num 个⼤⼩为 size 的元素开辟⼀块空间,并且把空间的每个字节初始化为0
  2. 与函数 malloc 的区别只在于 calloc 会在返回地址之前把申请的空间的每个字节初始化为0

例:

int main()
{
    int* pf = (int*)calloc(10, 40);   //10个元素,40字节
    if (pf != NULL)
    {
        //打印
        for (int i = 0; i < 10; i++)
        {
            printf("%d ", *(pf + i));
        }
        printf("\n\n\n");
        free(pf);
        pf = NULL;
    }
    else
    {
        perror("calloc");
    }
    return 0;
}

 结果:

如果我们对申请的内存空间的内容要求初始化,那么可以很⽅便的使⽤calloc函数来完成任务

3.4 realloc函数

void* realloc (void* ptr, size_t size);
  •  realloc函数的出现让动态内存管理更加灵活
  • 有时会我们发现过去申请的空间太⼩了,有时候我们⼜会觉得申请的空间过⼤了,那为了合理的时 候内存,我们⼀定会对内存的⼤⼩做灵活的调整。那 realloc 函数就可以做到对动态开辟内存⼤ ⼩的调整

 规则:

  1. ptr 是要调整的内存地址 
  2. size 调整之后新大小
  3. 返回值为调整之后的内存起始位置
  4. 这个函数调整原内存空间大小的基础上,还会将原来内存中的数据移动到新的空间

realloc在调整内存空间的是存在两种情况:

情况1:原有空间之后有⾜够⼤的空间

情况2:原有空间之后没有⾜够⼤的空间

 情况1

当是情况1的时候,要扩展内存就直接原有内存之后直接追加空间,原来空间的数据不发⽣变化

情况2

当是情况2的时候,原有空间之后没有⾜够多的空间时,扩展的⽅法是:在堆空间上另找⼀个合适⼤⼩ 的连续空间来使⽤。这样函数返回的是⼀个新的内存地址

这时就要改一下代码:

//代码1 - 直接将realloc的返回值放到ptr中 
ptr = (int*)realloc(ptr, 1000);

//代码2 - 先将realloc函数的返回值放在p中,不为NULL时,在放ptr中 
int* p = NULL;

p = realloc(ptr, 1000);

if (p != NULL)

{
    ptr = p;
}

4. 动态内存常见错误

4.1 对空指针(NULL)的解引用

void test()
 {
 int *p = (int *)malloc(INT_MAX/4);
 *p = 20;           //如果p的值是NULL,就会有问题 
 free(p);
 p=NULL;
 }

4.2 对开辟的空间造成越界访问

void test()
 {
 int i = 0;
 int *p = (int *)malloc(10*sizeof(int));
 if(NULL == p)
 {
 exit(EXIT_FAILURE);
 }
 for(i=0; i<=10; i++)   当i是10的时候越界访问 
 {
 *(p+i) = i;
 }
 free(p);
 p=NULL;
 }

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

void test()
{
 int a = 10;
 int *p = &a;
 free(p);      //非开辟动态内存
 p=NULL;

4.4 free释放一块动态开辟内存的一部分

void test()
 {
 int *p = (int *)malloc(100);
 p++;
 free(p);    //p不再指向动态内存的起始位置 
 p=NULL;
}

4.5 多次使用free释放(同一区域)

void test()
 {
 int *p = (int *)malloc(100);
 free(p);
 free(p);//重复释放 
 }

4.6 忘记释放(内存泄漏)

 void test()
 {
 int *p = (int *)malloc(100);
 if(NULL != p)
 {
 *p = 20;
 }
 }
 
 int main()
 {
 test();
 while(1);
 }

切记:动态开辟的空间⼀定要释放,并且正确释放;

5. 柔性数组

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

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

例:

typedef struct st_type
{
  int i;
  int a[0];     //柔性数组成员 
}type_a;

 有些编译器会报错⽆法编译可以改成:

typedef struct st_type
{
 int i;
 int a[];//柔性数组成员 
}type_a;

5.1 柔性数组特点

  1. 结构中的柔性数组成员前⾯必须⾄少⼀个其他成员
  2. sizeof 返回的这种结构大小不包括柔性数组的内存
  3. 包含柔性数组成员的结构⽤malloc()函数进⾏内存的动态分配,并且分配的内存应该⼤于结构的大小,以适应柔性数组的预期大小

例:

typedef struct st_type
{
 int i;
 int a[0];//柔性数组成员 
}type_a;
int main()
{
 printf("%d\n", sizeof(type_a));//输出的是4 
 return 0;
}

 5.2 柔性数组使用


#include <stdio.h>
#include <stdlib.h>
int main()
{
 int i = 0;
 type_a *p = (type_a*)malloc(sizeof(type_a)+100*sizeof(int));
 p->i = 100;
 for(i=0; i<100; i++)
 {
 p->a[i] = i;
 }
 free(p);
 return 0;
}

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

5.3 柔性数组好处

  1. 方便内存释放
  2. 利于访问速度

6. 小结

以上就是关于动态内存和柔性数组的内容了,具体还需宝子们去实践,如果觉得该博客对你有用的话,希望一键三连,点个关注不迷路,谢谢支持!

  • 21
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

坊钰

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值