{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.
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;
}
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;
}
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;
}
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)