10.一篇搞定动态内存分配,malloc函数如何实现动态内存分配,静态内存和动态内存

本人坚持更新C语言和数据结构知识,可以收藏+关注随时了解😜😜😜


目录

传统数组的缺点

为什么需要动态内存分配 

静态内存和动态内存的概念与区别

如何实现动态分配内存

动态分配数组

一维数组知识回顾

一维数组的构造

静态变量跨函数使用问题

1.静态变量不能跨函数使用

 2.动态内存可以跨函数使用


传统数组的缺点

  1. 数组的长度必须事先指定,且只能是常整数,不能是变量
  2. 传统形式定义的数组,该数组的内存程序员无法手动释放,数组一旦定义,系统位该数组分配的存储空间就一直存在,直到该函数运行结束,数组的空间才会被系统释放
  3. 数组的长度不能在函数运行的过程中不能动态改变,一旦定义,长度不能更改
  4. A函数定义的数组,在A函数运行期间可以被其他函数使用,但A函数运行完毕之后,A函数中的数组将无法再被其他函数使用(传统定义的数组不能跨函数使用)
void g(int *pArr, int len)
{
    pArr[2] = 88; // pArr[2] = a[2]
}
void f()
{
    int a[5] = {1, 2, 3, 4, 5};
    g(a, 5);
    //这20个字节程序员无法手动编程释放它
    //它只能在本函数运行完毕时系统自动释放
    printf("%d", a[2]);
    /*
    f函数定义的数组,在f函数运行期间可以被其他函数使用,
    但f函数运行完毕之后,f函数中的数组将无法再被其他函数使用
     */
}

为什么需要动态内存分配 

       动态数组很好解决了传统数组的4个缺陷

       传统数组也叫静态数组

静态内存和动态内存的概念与区别

        静态内存是指在程序开始运行时由编译器分配的内存,它的分配是在程序开始编译时完成的,不占用CPU资源。

       静态内存由系统自动分配,由系统自动释放

       静态内存是在栈分配的

       eg:基本类型,数组

       动态内存是程序员手动分配,手动释放(free函数)

       动态内存是在堆分配的

       动态内存分配需要指针和引用类型支持,静态不需要

       不需要预先分配存储空间

       分配的空间可以根据程序的需要扩大或缩小。

 如何实现动态分配内存

       需要使用到malloc函数

        malloc 是一个系统函数,它是 memory allocate 的缩写。其中memory是“内存”的意思,allocate是“分配”的意思。顾名思义 malloc 函数的功能就是“分配内存”。要调用它必须要包含头文件<stdlib.h>或者是<malloc.h>

       malloc函数返回一个void型指针,只有一个形参,并且形参是整型

       我们通过程序直观的去理解一下它

int main(int argc, char const *argv[])
{
  int i = 5; //分配了4个字节 静态分配
  int *p = (int *)malloc(4);
  free(p); //表示把p所指向的内存给释放掉
  // p本身的内存是静态内存,不能由free释放

  /*
    1.需要添加malloc.h这个头文件
    2.malloc函数只有一个形参,并且形参是整型
    3.‘4’表示请求系统为本程序分配4个字节
    4.malloc函数只能返回第一个字节的地址
    5.(int *)是将地址强转换告诉别人你第一个字节的地址指向的变量是几个字节
      int*就是4个字节,char*就是1个字节
    6.‘int *p = (int *)malloc(4);’分配了8个字节,p变量占4个字节,p所指向的内存也占4个字节
    7.p变量所占的内存是静态分配的,p所指向的内存是动态分配的

    可以这样理解:
        我们动态分配了100个字节
        返回了它的首地址为1000H,malloc返回的是无类型指针(void*),在使用时一定要强制转换为所需要的类型。
        我们将他强转成int *类型的,意味着动态分配的100个字节以4个字节来划分,就有100/4=25个变量
        所以
        int *p = (int *)malloc(100);
        就等于int *p =(int *)1000H;

    */

}

我们平常定义的int i = 5;这属于一个静态的分配内存,我们不能手动的去释放掉它的内存

int *p = (int *)malloc(4); 这属于是动态分配内存

我们来理解一下这段话

1.函数返回的是一个void型指针,因为我们定义的是int*p,p的数据类型为int*

所以我们需要将malloc的返回值强转为int*类型,所以是(int *)malloc();

2.malloc(4),这个4的意思是请求系统为程序分配4个字节,在之后的编程中,我们常常不写常数,而是写malloc(sizeof(int)),sizeof(int)就是4的意思

3.malloc函数只能返回第一个字节的地址。(int *)是将地址强转换告诉别人你第一个字节的地址指向的变量是几个字节,int*就是4个字节,char*就是1个字节

4.p变量所占的内存是静态分配的,p所指向的内存是动态分配的

再用更通俗的话来理解一下这句话

结社我们动态分配了100个字节

        返回了它的首地址为1000H,malloc返回的是无类型指针(void*),在使用时一定要强制转换为所需要的类型。

        我们将他强转成int *类型的,意味着动态分配的100个字节以4个字节(一个int占4个字节)来划分,就有100/4=25个变量

        所以源程序语句

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

        就等于int *p =(int *)1000H;

 通过画图,我们可以看出,动态分配的内存第一个字节地址为1000H,它被返回给了指针p,又因为它是int*类型的,所以是以4个字节为一个单元来划分内存

 动态分配数组

一维数组知识回顾

我们知道定义静态的一维数组时,数组的长度不能用变量定义,只可以写常数或者是通过define来定义 。int a[5]的意思就是定义了一个数组名为a,长度为5的数组,这个a其实就是一个指针常量,存储着数组第一个元素的地址

a[i]就等价于*(a+i);

 根据这个特性,我们就可以构造出动态的一维数组

一维数组的构造

int main(int argc, char const *argv[])
{
    int a[5];
    //如果int占4个字节的话,则本数组总共包含有20字节
    //每4个字节被当作了一个int变量来使用

    //--------动态创建数组---------
    int len;
    int *pArr;
    printf("请输入你想要存放的元素个数");
    scanf("%d", &len);
    pArr = (int *)malloc(sizeof(int) * len);
    //等价于写了int pArr[len];
    //该数组的数组名是pArr,该数组的每个元素是int类型,长度位len
    //因为它和一维数组的形式非常相似,pArr也存放着第一个元素的地址
    //*pArr指向的是前4个字节 *(pArr+1)指向后4个
    for (int i = 0; i < len; i++)
    {
        scanf("%d", &pArr[i]);
    }

    //输出数组
    printf("该数组为:\n");
    for (int j = 0; j < len; j++)
    {
        printf("%d\n", *(pArr + j));
    }
    free(pArr); //释放掉动态分配的数组
    return 0;
}

该数组的数组名是pArr,该数组的每个元素是int类型,长度位len,同样可以通过刚刚画的图来理解

每四个字节就是一个数组的元素,数组的长度为len,占的内存大小为sizeof(int)*len

静态变量跨函数使用问题

1.静态变量不能跨函数使用

        我们通过程序来说明

void f(int **q) // q是个指针变量,无论q是什么类型的指针,都只占4个字节
{
    int i = 5;
    //*q等价于p,q和**q都不等价于p
    *q = &i;
}
int main(int argc, char const *argv[])
{
    int *p;
    f(&p);
    printf("%d", *p);
    //本语句语法没有问题,但逻辑上有问题
    //因为静态变量在函数执行结束时就会释放,
    // 21行执行f(&p);之后i的空间就会被操作系统收回,这是它是不可读的
    //所以说,静态变量不能跨函数使用
    return 0;
}

本程序中,f函数中的变量i是一个静态变量,静态变量在函数执行完毕后会被操作系统收回,这时这个变量就不可读。这个程序中在执行完f(&p)之后,i的空间就被收回了,但是我们又打印了*p

p这时存放的时变量i的地址,但是i已经被收回,不能被读出来(这里有多级指针的知识,可以看我之前的博客http://t.csdn.cn/6VG9x

f函数在运行时

 f函数运行结束,i的空间被收回

 2.动态内存可以跨函数使用

静态内存不能跨函数使用主要原因就是静态变量在函数执行完毕后会被操作系统收回,动态内存则不会收回。动态内存是通过free函数手动释放。

void f(int **q)
{
    *q = (int *)malloc(sizeof(int));
    //*q = p
    //等价于p =(int *)malloc(szieof(int));
    **q = 5;
    //等价于*p = 5
    // free(*q);
}
int main(int argc, char const *argv[])
{
    int *p;
    f(&p);
    printf("%d", *p);
    return 0;
}

这样我们就可以在一个函数中定义变量,在另一个函数中也使用

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

爱打羽毛球的程序员

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

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

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

打赏作者

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

抵扣说明:

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

余额充值