DEV Community

Cover image for Não ter leak de memória é bem fácil, na real
Andrey_vdl
Andrey_vdl

Posted on

Não ter leak de memória é bem fácil, na real

#c

{INFO}: Esse post não tem relação nenhuma com o canal Bem fácil, na real, só achei que o título combinava 🙂

Se você coda em uma linguagem com gerenciamento manual de memória já deve ter se deparado com um double free ou com o famoso memory leak.

Valgrind program leak

E por mais que existam Arenas, smart pointers e Garbage Collectors muita gente ainda falha em criar um programa sem vazamentos.

A solução é muito simples e provavelmente uma frase que você ouviu da sua mãe quando era criança "Coloque tudo de volta em seu devido lugar", mais especificadamente a parte "seu devido lugar", ou seja, aloque e desaloque memória em seu devido lugar.

int main()
{
    char *ptr = malloc(100 * sizeof(char));
    size_t addr = (size_t)ptr;

    for (int i = 0; i < sizeof("Hello, World!\n"); ++i, ++ptr)
        *ptr = "Hello, World!\n"[i];
    *ptr = '\0';

    ptr = (char*)addr;
    printf(ptr);

    free(ptr);
    return 0;
}
Enter fullscreen mode Exit fullscreen mode

Nesse exemplo a memória alocada para ptr é desalocada no mesmo local que ptr.

typedef struct {
    char *ptr;
} Object;

Object objectInit(void)
{
    Object o;
    o.ptr = malloc(100 * sizeof(char));
    return o;
}

int main()
{
    Object obj = objectInit();
    size_t addr = (size_t)obj.ptr;

    for (int i = 0; i < sizeof("Hello, World!\n"); ++i, ++obj.ptr)
        *(obj.ptr) = "Hello, World!\n"[i];
    *(obj.ptr) = '\0';

    obj.ptr = (char*)addr;
    printf(obj.ptr);

    free(obj.ptr);
    return 0;
}
Enter fullscreen mode Exit fullscreen mode

Já nesse outro a alocação não pode ser liberada na mesma função, então vamos liberar na função mais próxima dela, ou seja, no local onde a função alocadora foi chamada.

E isso é gerenciamento de memória for dummies... foi tão difícil assim?... Ah, sim, faltou falar de "e se a alocação falhar". Bom, se a alocação falhar, a gente só libera o que já foi alocado.

Fique avisado que o código que será visto a seguir é o maior pecado da programação, devido a algo tão proibido que somente os maiores programadores são permitidos de usar.


typedef struct {
    char *ptr;
    // ... finge que tem outros 100 atributos aqui.
} Object;

Object* objectNew(void)
{
    Object *o = NULL;

    o = calloc(1, sizeof(Object));
    if (!o)
        return NULL;

    o->ptr = malloc(100 * sizeof(char));
    if (!o->ptr)
        goto error_return;
    // ... inicializa os atributos

    return o;
error_return:
    // ... libera os atributos
    if (o->ptr) free(o.ptr);
    free(o);
    return NULL;
}

void objectDelete(Object *o)
{
    if (!o)
        return;
    // ... libera os atributos
    if (o->ptr) free(o->ptr);
}

void objectDelete2(Object **o)
{
    Object *temp = NULL;

    if (!o || !(*o))
        return;
    temp = *o;
    // ...
    if (temp->ptr) free(temp->ptr);
    free(*o);
    *o = NULL;
}

Object* objectDelete3(Object *o)
{
    if (!o)
        return o;
    // ...
    if (o->ptr) free(o->ptr);
    free(o);
    return NULL;
}

int main()
{
    Object *obj = objectNew();
    Object *obj2 = objectNew();
    Object *obj3 = objectNew();
    // ...
    objectDelete(obj);
    free(obj);
    // delete considera que o objeto principal/container é responsabilidade do usuário
    objectDelete2(&obj2);
    // delete2 considera já definir o objeto principal/container para nulo dentro da função.
    obj3 = objectDelete3(obj3);
    // delete3 prefere deixa o usuário decidir se o objeto vai ser definido para nulo.
    // um delete4 poderia ter flags que decidem se o objeto vai ser liberado ou não e se vai definido para nulo ou não.
    return 0;
}
Enter fullscreen mode Exit fullscreen mode

Nesse último exemplo temos uma função que aloca um objeto e utiliza o temido GOTO em caso de erro, porque imagina o trabalho de ter que escrever cada if para cada possível erro no meio do código, é muito mais fácil pular para uma "zona de erro" e lidar com a liberação de recursos que estavam sendo utilizados.

Além da função que aloca o objeto, temos também funções que liberam os recursos após eles serem utilizados, cada uma com sua curiosa implementação um pouco diferente.

Bom, então é isso, se você simplesmente seguir essa regra é 99,99999% de certeza que você não vai ter um único leak de memória (os 0,00001% são de leaks internos de funções de bibliotecas).

Comentem casos que não foram abordados, discordâncias da minha opinião que podemos classificar minimamente como polêmica e é claro, compartilhe para seus amigos.

Top comments (0)