剑侠盟·网游特攻队

  • 首页
  • 门派特辑
  • 情缘系统
  • 帮派战报
  • HOME> 门派特辑> 【C语言】动态内存管理:malloc、calloc、realloc、free
    【C语言】动态内存管理:malloc、calloc、realloc、free
    门派特辑

    本篇介绍一下C语言中的malloc/calloc/realloc。 使用这些函数需要包含头文件。malloc/calloc/realloc申请的空间都是 堆区的。1.malloc和free1.1 mallocC语言提供了一个动态内存开辟的函数malloc,函数原型如下。

    代码语言:javascript复制void* malloc(size_t size); //size的单位是字节这个函数向内存申请一块连续可用的空间,并返回指向这款空间的指针。

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

    如果开辟失败,则返回NULL,因此malloc的返回值一定要做检查。

    返回值类型是void*,所以malloc函数并不知道开辟空间的类型,具体要在使用的时候自己来决定。要强制类型转换。

    y如果参数size为0,malloc的行为是标准未定义的,取决于编译器。

    比如我们申请20个字节,用来存放整形。下面两种写法是一样的。

    代码语言:javascript复制int* p0 = (int*)malloc(20);

    int* p1 = (int*)malloc(5 * sizeof(int));然后检查一下返回值。

    代码语言:javascript复制int* p0 = (int*)malloc(20);

    //检查返回值

    if (p0 == NULL) //申请失败

    {

    perror("malloc fail");

    return 1;

    }申请成功就可以使用空间了。当作数组使用就行。

    然后我们往里面存一些值进去

    1.2 free申请的空间不要了,还要手动换回去。C语言提供了另一个函数free,专门是用来做动态内存释放和回收的,函数原型如下。

    代码语言:javascript复制void free(void* ptr);free函数用来释放动态开辟内存。ptr传的是要释放的内存空间的起始地址。

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

    如果参数ptr是NULL指针,则函数什么都不做。

    拿前面的p0举例,我们现在要释放它。

    代码语言:javascript复制free(p0); //把空间还给操作系统此时p0指针什么都没指,是个野指针,所以我们还要手动把p0置为NULL,这一步千万不能忘。

    代码语言:javascript复制free(p0); //把空间还给操作系统

    p0 = NULL; //置空 所以我们在使用申请的空间的时候,要记得记录起始位置指针,最好就是别改变起始位置指针的指向。

    2.calloc和realloc2.1 callocC语言还提供了一个函数叫做calloc,这个函数也是用来动态内存分配的,原型如下。

    代码语言:javascript复制void* calloc(size_t num, size_t size); 函数功能是为num个大小为size的元素开辟空间,并且把空间的每个元素初始化为0。

    与malloc函数的区别只在于calloc会在返回地址之前把申请的空间的每个字节初始化为全0。

    比如我们想向内存申请5个整形的空间。

    代码语言:javascript复制int* p1 = (int*)calloc(5, 4);

    int* p1 = (int*)calloc(5, sizeof(int)); //这样写也可以同样的,要检查返回值。

    代码语言:javascript复制int* p1 = (int*)calloc(5, sizeof(int));

    if (p1 == NULL)

    {

    perror("calloc fail");

    return 1;

    }我们通过调试看p的内存,发现都初始化为了0。

    除了calloc能初始化,其他的和malloc没什么区别。

    最后也是一样,不用了就free,并且把p1置空。

    代码语言:javascript复制free(p1);

    p1 = NULL;2.2 reallocrealloc的出现让动态内存管理更加灵活,它可以调整动态开辟内存的大小,函数原型如下。

    代码语言:javascript复制void* realloc(void* ptr, size_t size);ptr是要调整的内存地址,size是调整之后新的大小,返回值为调整之后的内存起始位置。

    比如说我要把前面的p0申请的20个空间扩展到40个。有下面的两种情况。

    情况一:直接在原来的基础上往后扩展就行,然后返回起始地址。

    情况二 :

    1.在内存里重新找一块区域,这块区域直接满足40个字节的需求。

    2.把原来的内容都拷贝在这个新内存里。

    3.释放旧空间。(realloc主动释放,不用自己手动释放)

    4.返回新的内存空间的起始地址。

    情况三:新找的空间也不够,直接返回NULL。

    所以我们在使用realloc的时候就不能直接像下面这样写。

    代码语言:javascript复制int* p0 = (int*)malloc(20);

    if (p0 == NULL)

    {

    perror("malloc fail");

    return 1;

    }

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

    {

    *(p0 + i) = i;

    }

    int* p0 = (int*)realloc(p0, 40); //错误写法因为一旦扩容失败,返回NULL,把NULL放在p0里,不但没有调整空间,原来的数据也丢失了。所以不能直接用原来的指针直接接受扩容后的新地址,要先进行一个判断,如下。

    代码语言:javascript复制int* newptr = (int*)realloc(p0, 40);//新指针接收

    if (newptr != NULL) //判断是否成功

    {

    p0 = newptr; //成功了再用原来的指针接管扩容后的空间

    }

    else //失败

    {

    perror("realloc fail");

    //...

    } 不用了同样要free释放。

    realloc函数的第一个参数如果传NULL指针的话,可以实现和malloc一样的功能。

    代码语言:javascript复制realloc(NULL, 20); //== malloc(20);3.常见的动态内存错误3.1 对NULL指针解引用代码语言:javascript复制void test1()

    {

    //INT_MAX是int最大值,此时空间开辟绝对是失败的

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

    *p = 20; //开辟空间失败返回NULL,不能对NULL解引用

    free(p);

    p = NULL;

    }所以我们一定要判断malloc的返回值。

    3.2 对动态开辟的空间越界访问代码语言:javascript复制void test2()

    {

    int i = 0;

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

    if (p == NULL)

    {

    return;

    }

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

    {

    *(p + i) = i; //当i为10时越界访问

    }

    free(p);

    p = NULL;

    }malloc申请的空间和数组非常相似,不可以越界访问。

    3.3 对非动态开辟的内存使用free释放代码语言:javascript复制void test3()

    {

    int a = 10;

    int* p = &a;

    free(p);

    p = NULL;

    }不是动态开辟的空间不需要free,int的空间在栈上,也不在堆上,动态申请的才在堆区上。free只释放动态开辟的。

    3.4 使用free释放一块动态开辟内存的一部分代码语言:javascript复制void test4()

    {

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

    p++;

    free(p); //p此时不再指向起始位置

    p = NULL;

    }free(ptr)我们前面说过ptr必须是起始位置。只释放一部分程序会出错崩溃。

    3.5 对同一块内存多次释放代码语言:javascript复制void test5()

    {

    int* p = malloc(100);

    free(p);

    free(p); //不可重复释放

    }这种情况最好避免发生的方法就是free之后赶紧置空。

    代码语言:javascript复制void test5()

    {

    int* p = malloc(100);

    free(p);

    p = NULL;

    free(p); //free空,什么都不发生

    }3.6 动态开辟的内存忘记释放代码语言:javascript复制void test6()

    {

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

    if (NULL != p)

    {

    *p = 20;

    }

    }代码语言:javascript复制int main()

    {

    test6();

    //出test6函数后p就销毁了,想释放现在也释放不了

    while (1);//程序死循环

    return 0;

    }自己已经不用了,不释放不还给操作系统,别人也不能用,会发生内存泄漏问题。

    但是我们不用free释放,程序运行结束的时候,也会由操作系统回收,如果程序一直不退出呢?所以最好别忘记释放。

    本篇分享就到这里,拜拜~

    全市各医院放号的时间、规则是什么样的?
    正月初一()—

    友情链接:


    Copyright © 2022 剑侠盟·网游特攻队 All Rights Reserved.