调用函数分配内存

时间:2021-09-23 18:50:38

前言:

        在实际项目中,经常会遇到一些需要多次重复进行某种动态内存管理的情形。

        例如,为了尽可能地解耦、尽可能少地使用全局变量、或者尽可能高效地维护某一类相似的内存管理操作代码,可能有需要通过函数调用的方式分配内存,并将分配的内存返回给发起调用的函数的情形。


如果要在函数调用中进行模块内存的释放,可以使用如下方式:

#include <string.h>
unsigned char my_free(int* addr);//method to free a block of memory

int main(void *pData)
{
    int *addr = (int*)malloc(10);
    //.... some  process
    if(my_free(addr))
    {
         printf("Error occured");
    }
}

unsigned char my_free(int* addr)
{
    if(!addr)
    {
        free(addr); 
        return 1;
    }
    return 0;
}

但如果认为可以类似地,采用类比上面(即如下)这种方式进行分配,则大错特错了!

unsigned char my_alloc(int* addr, int size)
{
    int *tmp_addr = (void*)0;
    tmp_addr = (int*)malloc(size);
    if(!tmp_addr)
    {
        addr = (VOID*)0;
        return 0;
    }
    addr = tmp_addr;
    return 0;
}

int main(void* pData)
{
    int* addr = (void*)0;
    if(my_alloc(addr, 10))
    {
        printf("my_alloc() method ok");
    }

    return 0;
}

分析:

        1. 实际上在上述代码中,方法调用 my_alloc(addr, 10) 中,addr 传入的是一个在调用外申明的地址,该地址指向/存放的地址为空(即(void*(0));

        2. 在调用该函数 my_alloc(addr, 10) 时,函数在开辟该函数运行的栈空间 stack_my_alloc_ 时,就会将 addr 的值也即其地址(而非其所存储的值/所指向的地址)进行复制,接着在 进行 addr = tmp_addr 的赋值时,将addr的副本变量的存放地址从原来的 &addr 直接修改为了tmp_addr所指向的地址,而非将 addr 的值修改为tmp_addr所指地址;

        3. 上述行为也导致了这块已经开辟好的内存空间的指针,被赋予一个该子函数栈空间中的一个变量(存在无法释放的可能);

        4. 在退出该函数时,原来addr 存放的地址仍然是 &addr, 其所指向地址仍然是 (void*)0, 而那个副本变量在退出调用时已经不复存在,因此这块被申请的内存永远得不到释放(部分做了深度优化的编译器可以检查并在退出时进行free的情况除外) 。


结论:

        这种错误的写法,有两个后果:一,达不到想要的malloc目的; 二,导致内存耗尽(被吞噬、泄露),造成不断死机


正确姿势:

        从上述分析可以看出,显然在函数退出调用时,应将 tmp_addr 所指向的内存地址 存入 addr 中,而不是将 &addr 的副本变量(仅存活于该调用栈空间中)由原来在栈空间中的地址(绝对不等于&addr)重新指向tmp_addr。

        因此,在不利用全局变量的情况下,必须使用指向指针的指针来进行调用,这个动态分配内存的子函数就应该写成:

unsigned char my_alloc(int** addr, int size)
{
    int *tmp_addr = (void*)0;
    tmp_addr = (int*)malloc(size);
    if(!tmp_addr)
    {
        *addr = (VOID*)0;
        return 0;
    }
    *addr = tmp_addr;
    return 0;
}

调用方式为:
int main(void* pData)
{
    int* addr = (void*)0;
    if(my_alloc(&addr, 10))
    {
        printf("my_alloc() method ok");
    }
    //do something with addr

    return 0;
}