Gerenciamento de Memória Dinâmica
Memória dinâmica é como alugar um apartamento — você solicita quando precisa (malloc), e devolve quando termina (free). Se você nunca devolve, continua ocupando recursos — isso é um vazamento de memória.
Por Que Memória Dinâmica
Variáveis locais na pilha desaparecem quando uma função retorna, e tamanhos de array devem ser determinados em tempo de compilação. Quando o tamanho dos dados só é conhecido em tempo de execução, ou você precisa que os dados persistam entre chamadas de função, você precisa de memória dinâmica no heap.
int n;
scanf("%d", &n);
int arr[n];
malloc.
malloc e free
malloc aloca um número especificado de bytes no heap e retorna void *. Você deve fazer free da memória quando terminar.
int *p = (int *)malloc(sizeof(int) * 5);
if (p == NULL) {
printf("Falha na alocação de memória\n");
return 1;
}
for (int i = 0; i < 5; i++) {
p[i] = i * 10;
}
for (int i = 0; i < 5; i++) {
printf("%d ", p[i]);
}
free(p);
p = NULL;
0 10 20 30 40
malloc não inicializa a memória — o conteúdo são valores lixo. Sempre verifique o valor de retorno quanto a NULL após a alocação. Definir o ponteiro como NULL após free é uma boa prática para evitar ponteiros pendentes.
calloc
calloc aloca memória e inicializa cada bit com 0. Seus parâmetros são o número de elementos e o tamanho de cada elemento.
int *p = (int *)calloc(5, sizeof(int));
if (p) {
for (int i = 0; i < 5; i++) {
printf("%d ", p[i]);
}
free(p);
}
0 0 0 0 0
malloc vs calloc:
| Função | Inicialização | Parâmetros |
|---|---|---|
malloc |
Sem inicialização | Total de bytes |
calloc |
Zerado | Quantidade de elementos, tamanho de cada elemento |
realloc
realloc redimensiona memória previamente alocada — pode expandir ou reduzir.
int *p = (int *)malloc(sizeof(int) * 3);
p[0] = 10; p[1] = 20; p[2] = 30;
int *tmp = (int *)realloc(p, sizeof(int) * 5);
if (tmp) {
p = tmp;
p[3] = 40;
p[4] = 50;
for (int i = 0; i < 5; i++) {
printf("%d ", p[i]);
}
free(p);
}
10 20 30 40 50
realloc pode retornar um novo endereço (os dados originais são copiados automaticamente) ou o mesmo endereço. Nunca escreva p = realloc(p, ...) — se falhar e retornar NULL, o ponteiro original é perdido. Use uma variável temporária para receber o resultado.
Array 1D Dinâmico
Exemplo
#include <stdio.h>
#include <stdlib.h>
int main(void) {
int n;
printf("Digite a quantidade de elementos: ");
scanf("%d", &n);
int *arr = (int *)calloc(n, sizeof(int));
if (!arr) return 1;
for (int i = 0; i < n; i++) {
arr[i] = (i + 1) * (i + 1);
}
for (int i = 0; i < n; i++) {
printf("%d ", arr[i]);
}
printf("\n");
free(arr);
arr = NULL;
return 0;
}
Digite a quantidade de elementos: 5
1 4 9 16 25
Array 2D Dinâmico
Existem duas abordagens comuns para implementar arrays 2D dinâmicos.
Abordagem 1: Array de Ponteiros (Cada Linha Alocada Independentemente)
int rows = 3, cols = 4;
int matrix = (int )malloc(sizeof(int *) * rows);
for (int i = 0; i < rows; i++) {
matrix[i] = (int *)calloc(cols, sizeof(int));
}
matrix[1][2] = 99;
printf("%d\n", matrix[1][2]);
for (int i = 0; i < rows; i++) {
free(matrix[i]);
}
free(matrix);
99
Abordagem 2: Memória Contígua (Única Alocação)
int rows = 3, cols = 4;
int *buf = (int *)calloc(rows * cols, sizeof(int));
int matrix = (int )malloc(sizeof(int *) * rows);
for (int i = 0; i < rows; i++) {
matrix[i] = buf + i * cols;
}
matrix[2][3] = 77;
printf("%d\n", matrix[2][3]);
free(matrix);
free(buf);
77
Exemplo
#include <stdio.h>
#include <stdlib.h>
int main(void) {
int rows = 3, cols = 4;
int m = (int )malloc(sizeof(int *) * rows);
int *buf = (int *)calloc(rows * cols, sizeof(int));
for (int i = 0; i < rows; i++) {
m[i] = buf + i * cols;
}
int val = 1;
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
m[i][j] = val++;
}
}
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
printf("%3d", m[i][j]);
}
printf("\n");
}
free(m);
free(buf);
return 0;
}
1 2 3 4
5 6 7 8
9 10 11 12
Erros Comuns de Memória Dinâmica
Esquecer o free — Vazamento de Memória
void leak(void) {
int *p = (int *)malloc(sizeof(int) * 100);
}
Quando a função termina, a variável local p desaparece, mas os 100 ints de memória do heap ainda estão ocupados e nunca podem ser liberados.
Double free
int *p = (int *)malloc(sizeof(int));
free(p);
free(p);
free na mesma memória duas vezes é comportamento indefinido e pode causar crash no programa. Defina o ponteiro como NULL imediatamente após free — chamar free(NULL) é seguro.
Usar Memória Liberada
int *p = (int *)malloc(sizeof(int));
*p = 42;
free(p);
printf("%d\n", *p);
Acesso Fora dos Limites
int *p = (int *)malloc(sizeof(int) * 5);
p[5] = 100;
Foram alocados 5 elementos com índices de 0 a 4, então p[5] está fora dos limites.
Introdução ao valgrind
Valgrind é uma ferramenta de verificação de memória no Linux que pode detectar vazamentos de memória, acessos fora dos limites e leituras não inicializadas.
gcc -g -o myapp myapp.c
valgrind --leak-check=full ./myapp
Saída típica:
==12345== HEAP SUMMARY:
==12345== in use at exit: 400 bytes in 1 blocks
==12345== total heap usage: 2 allocs, 1 frees, 800 bytes allocated
==12345==
==12345== 400 bytes in 1 blocks are definitely lost in loss record 1 of 1
==12345== at 0x4C2FB0F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==12345== by 0x10915E: leak (myapp.c:5)
❓ Perguntas Frequentes
P: Quais valores estão na memória retornada por malloc? R: Indefinidos — são dados lixo de usos anteriores. Use calloc se precisar de memória zerada, ou chame memset manualmente.
P: Um ponteiro ainda tem valor após free? R: free apenas libera a memória — não muda o valor do ponteiro. Ele ainda contém aquele endereço (ponteiro pendente). Por isso você deve defini-lo como NULL imediatamente após liberar.
P: realloc perde os dados originais ao expandir? R: Não. realloc garante que os dados originais são preservados (pelo menos min(tamanho_antigo, tamanho_novo) bytes). Se mudar para um novo endereço, copia automaticamente.
P: Posso fazer free de uma variável na pilha? R: Absolutamente não. free só pode liberar memória do heap retornada por malloc/calloc/realloc. Chamar free em um endereço na pilha é comportamento indefinido.
📖 Resumo
malloc(size)aloca sem inicializar,calloc(n, size)aloca e zera,realloc(ptr, size)redimensiona- Para arrays 2D dinâmicos, recomenda-se a abordagem "única alocação contígua + mapeamento de linhas com array de ponteiros"
- malloc e free devem sempre ser pareados — esquecer o free causa vazamentos de memória
- Erros comuns: vazamentos, double free, ponteiros pendentes, acessos fora dos limites
- valgrind pode detectar automaticamente problemas de memória — use-o regularmente durante o desenvolvimento
📝 Exercícios
- Escreva um programa: o usuário digita n, aloca dinamicamente n ints, lê n números, ordena e exibe, depois libera a memória
- Escreva funções de array 2D dinâmico:
int create_matrix(int rows, int cols)evoid free_matrix(int m, int rows). Em main, crie uma matriz 4x5, atribua valores e imprima - Crie intencionalmente um vazamento de memória no seu código, depois use valgrind (ou Dr. Memory) para detectá-lo e examine a saída



