malloc · calloc · realloc · free — gerenciando memória em tempo de execução
Até agora, todos os arrays que você declarou tinham tamanho fixo em tempo de compilação:
int notas[30]; // sempre 30 — mesmo se só houver 3 alunos float precos[1000]; // sempre 1000 — mesmo se forem 5 produtos
Esse modelo tem dois problemas sérios: ou você desperdiça memória (declarou 1000, usa 5), ou fica sem espaço (declarou 30, mas vieram 31 alunos). A alocação dinâmica resolve os dois.
malloc, calloc e realloc da biblioteca <stdlib.h>.
| Característica | Estática (stack) | Dinâmica (heap) |
|---|---|---|
| Tamanho definido em | Tempo de compilação | Tempo de execução |
| Gerenciamento | Automático — criada e destruída com a função | Manual — você aloca e você libera |
| Capacidade | Limitada (stack típica: 1–8 MB) | Limitada apenas pela RAM disponível |
| Flexibilidade | Baixa — tamanho fixo | Alta — pode crescer e encolher |
| Risco | Stack overflow (recursão profunda) | Memory leak se não liberar com free |
<stdlib.h> é obrigatório. As funções malloc, calloc, realloc e free são declaradas nesse cabeçalho. Sem ele, o compilador não conhece os protótipos e pode gerar erros ou avisos.
malloc — Alocar um Bloco de Memóriamalloc(size) (memory allocation) aloca um bloco contíguo de size bytes no heap e retorna um ponteiro void * para o início do bloco. O conteúdo não é inicializado — pode conter qualquer valor. Retorna NULL se a alocação falhar.
void *malloc(size_t size);
/* Uso típico: alocar n elementos de um tipo */
tipo *p = (tipo *) malloc(n * sizeof(tipo));
sizeof(tipo)? O tamanho de cada tipo pode variar entre compiladores e plataformas. sizeof(int) garante o tamanho correto independente do sistema. Nunca escreva valores fixos como malloc(n * 4) — use sempre malloc(n * sizeof(int)).
#include <stdio.h>
#include <stdlib.h>
int main() {
int n;
printf("Quantos alunos? ");
scanf("%d", &n);
/* Aloca exatamente n inteiros no heap */
int *notas = (int *) malloc(n * sizeof(int));
/* SEMPRE verifique se a alocação teve sucesso */
if (notas == NULL) {
printf("Erro: memória insuficiente!\n");
return 1;
}
/* Usa exatamente como um array normal */
for (int i = 0; i < n; i++) {
printf("Nota do aluno %d: ", i + 1);
scanf("%d", ¬as[i]);
}
int soma = 0;
for (int i = 0; i < n; i++)
soma += notas[i];
printf("Média: %.1f\n", (float)soma / n);
/* Libera a memória — obrigatório! */
free(notas);
notas = NULL; // boa prática: evita dangling pointer
return 0;
}
| Região | Conteúdo | Endereço (exemplo) |
|---|---|---|
| Stack | Variável notas (o ponteiro) |
0x7fff...10 |
| Heap | Bloco de n × 4 bytes (os dados) |
0x5555...a0 |
O ponteiro notas na stack aponta para o bloco de dados no heap. Após free(notas), o bloco no heap é devolvido ao sistema — mas o ponteiro ainda contém o endereço antigo até você atribuir NULL.
calloc — Alocar e Zerarcalloc(quantidade, tamanho) (contiguous allocation) aloca espaço para quantidade elementos de tamanho bytes cada, e inicializa todos os bytes com zero. É equivalente a malloc seguido de memset(..., 0, ...), mas geralmente mais eficiente.
void *calloc(size_t quantidade, size_t tamanho); /* Uso típico */ int *v = (int *) calloc(n, sizeof(int)); // todos os elementos começam em 0
#include <stdio.h>
#include <stdlib.h>
int main() {
int n = 5;
/* malloc: conteúdo indefinido (lixo) */
int *vm = (int *) malloc(n * sizeof(int));
printf("malloc (sem init): ");
for (int i = 0; i < n; i++)
printf("%d ", vm[i]); // valores imprevisíveis!
printf("\n");
/* calloc: todos zeros garantidos */
int *vc = (int *) calloc(n, sizeof(int));
printf("calloc (zeros): ");
for (int i = 0; i < n; i++)
printf("%d ", vc[i]); // sempre 0 0 0 0 0
printf("\n");
free(vm);
free(vc);
return 0;
}
Use malloc quando… |
Use calloc quando… |
|---|---|
| Você vai preencher todos os elementos imediatamente | Os elementos precisam começar em zero (contadores, flags, matrizes) |
Você lê o array com scanf logo após alocar |
Parte do array pode ficar sem inicialização explícita |
| Desempenho é crítico e zeros não são necessários | Você quer garantia de estado inicial limpo |
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main() {
/* Frequência de cada letra (a-z) — começa tudo em 0 */
int *freq = (int *) calloc(26, sizeof(int));
if (freq == NULL) return 1;
char frase[] = "alocacao dinamica em c";
for (int i = 0; frase[i] != '\0'; i++) {
if (frase[i] >= 'a' && frase[i] <= 'z')
freq[frase[i] - 'a']++; // índice 0='a', 1='b', ...
}
printf("Frequência de letras em \"%s\":\n", frase);
for (int i = 0; i < 26; i++) {
if (freq[i] > 0)
printf(" '%c': %d\n", 'a' + i, freq[i]);
}
free(freq);
return 0;
}
realloc — Redimensionar um Blocorealloc(ptr, novo_tamanho) redimensiona um bloco previamente alocado com malloc ou calloc. Se possível, expande o bloco no mesmo lugar; caso contrário, aloca um novo bloco, copia os dados antigos e libera o bloco original. Retorna NULL se falhar — sem liberar o bloco original.
void *realloc(void *ptr, size_t novo_tamanho);
realloc: Se você escrever p = realloc(p, ...) e a alocação falhar, realloc retorna NULL sem liberar o bloco original. Ao atribuir NULL a p, você perde a referência ao bloco original — causando um memory leak. Sempre use uma variável temporária:
/* ❌ ERRADO — memory leak se realloc falhar */ p = realloc(p, novo_tamanho); /* ✅ CORRETO — preserva o ponteiro original em caso de falha */ int *temp = realloc(p, novo_tamanho); if (temp == NULL) { printf("Erro de alocação!\n"); free(p); // libera o bloco original antes de sair return 1; } p = temp;
#include <stdio.h>
#include <stdlib.h>
int main() {
int capacidade = 4; // tamanho inicial
int tamanho = 0; // elementos realmente usados
int *lista = (int *) malloc(capacidade * sizeof(int));
if (lista == NULL) return 1;
int valor;
printf("Digite inteiros (0 para parar):\n");
while (1) {
printf(" > ");
scanf("%d", &valor);
if (valor == 0) break;
/* Se cheio, dobra a capacidade */
if (tamanho == capacidade) {
capacidade *= 2;
int *temp = (int *) realloc(lista, capacidade * sizeof(int));
if (temp == NULL) {
printf("Sem memória!\n");
free(lista);
return 1;
}
lista = temp;
printf(" [capacidade expandida para %d]\n", capacidade);
}
lista[tamanho++] = valor;
}
printf("\nLista (%d elementos): ", tamanho);
for (int i = 0; i < tamanho; i++)
printf("%d ", lista[i]);
printf("\n");
free(lista);
return 0;
}
ArrayList do Java e pela maioria das estruturas dinâmicas de alto desempenho. Ao dobrar (em vez de somar 1), o custo amortizado de cada inserção permanece O(1) — o número de realocações cresce apenas logaritmicamente com o total de elementos.
free — Liberar Memóriafree(ptr) devolve ao sistema o bloco de memória previamente alocado com malloc, calloc ou realloc. Após o free, o bloco não pertence mais ao programa — acessá-lo é comportamento indefinido.
#include <stdlib.h> int *p = (int *) malloc(10 * sizeof(int)); /* ... usa p ... */ free(p); // devolve os 40 bytes ao sistema p = NULL; // evita dangling pointer
free| Regra | O que acontece se violar |
|---|---|
Só libere ponteiros obtidos de malloc/calloc/realloc |
Comportamento indefinido — corrupção do heap |
Não chame free duas vezes no mesmo ponteiro (double free) |
Comportamento indefinido — pode travar ou comprometer a segurança |
Não use o ponteiro após free (use after free) |
Comportamento indefinido — pode acessar dados de outra variável |
free(NULL) é seguro e não faz nada |
— (operação segura por definição) |
#include <stdio.h> #include <stdlib.h> /* ❌ ERRADO — cada chamada vaza memória */ void processarDados_errado(int n) { int *buf = (int *) malloc(n * sizeof(int)); /* ... processa ... */ return; // buf não foi liberado! memória perdida para sempre } /* ✅ CORRETO — libera antes de retornar */ void processarDados_correto(int n) { int *buf = (int *) malloc(n * sizeof(int)); if (buf == NULL) return; /* ... processa ... */ free(buf); // libera antes de sair da função buf = NULL; }
Matrizes bidimensionais também podem ser alocadas dinamicamente. Há duas abordagens principais:
Aloca todos os elementos em um único bloco contíguo e usa aritmética de índice para simular linhas e colunas:
#include <stdio.h>
#include <stdlib.h>
int main() {
int linhas = 3, colunas = 4;
/* Bloco único de linhas × colunas elementos */
int *matriz = (int *) malloc(linhas * colunas * sizeof(int));
if (matriz == NULL) return 1;
/* Preenche com linha × coluna */
for (int i = 0; i < linhas; i++)
for (int j = 0; j < colunas; j++)
matriz[i * colunas + j] = i * 10 + j; // fórmula do índice
/* Imprime */
printf("Matriz %dx%d:\n", linhas, colunas);
for (int i = 0; i < linhas; i++) {
for (int j = 0; j < colunas; j++)
printf("%4d", matriz[i * colunas + j]);
printf("\n");
}
free(matriz); // um único free — simples!
return 0;
}
Permite linhas com tamanhos diferentes (jagged array) — muito útil para triângulos, grafos e matrizes esparsas:
#include <stdio.h>
#include <stdlib.h>
int main() {
int linhas = 3;
int cols[] = {2, 4, 3}; // cada linha tem tamanho diferente
/* Aloca array de ponteiros (uma linha cada) */
int **m = (int **) malloc(linhas * sizeof(int *));
if (m == NULL) return 1;
/* Aloca cada linha individualmente */
for (int i = 0; i < linhas; i++) {
m[i] = (int *) malloc(cols[i] * sizeof(int));
if (m[i] == NULL) { /* tratar erro */ return 1; }
for (int j = 0; j < cols[i]; j++)
m[i][j] = (i + 1) * 10 + j;
}
/* Imprime */
for (int i = 0; i < linhas; i++) {
printf("Linha %d (%d elem): ", i, cols[i]);
for (int j = 0; j < cols[i]; j++)
printf("%d ", m[i][j]);
printf("\n");
}
/* Libera: cada linha e depois o array de ponteiros */
for (int i = 0; i < linhas; i++)
free(m[i]);
free(m);
return 0;
}
Um programa robusto que usa alocação dinâmica segue sempre este padrão:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct {
char nome[50];
float salario;
int anos;
} Funcionario;
int main() {
int n;
printf("Número de funcionários: ");
scanf("%d", &n);
/* 1. ALOCAR — e verificar imediatamente */
Funcionario *equipe = (Funcionario *) malloc(n * sizeof(Funcionario));
if (equipe == NULL) {
fprintf(stderr, "Erro: memória insuficiente para %d funcionários.\n", n);
return 1;
}
/* 2. USAR */
for (int i = 0; i < n; i++) {
printf("\n--- Funcionário %d ---\n", i + 1);
printf("Nome: "); scanf(" %49[^\n]", equipe[i].nome);
printf("Salário: "); scanf("%f", &equipe[i].salario);
printf("Anos: "); scanf("%d", &equipe[i].anos);
}
/* 3. PROCESSAR */
float total = 0;
int senioridade = 0;
for (int i = 0; i < n; i++) {
total += equipe[i].salario;
if (equipe[i].anos >= 5) senioridade++;
}
printf("\n=== Resumo da Equipe ===\n");
printf("Total de funcionários: %d\n", n);
printf("Folha total: R$%.2f\n", total);
printf("Salário médio: R$%.2f\n", total / n);
printf("Seniores (≥5 anos): %d\n", senioridade);
/* 4. LIBERAR — sempre ao final */
free(equipe);
equipe = NULL;
return 0;
}
| Função | Assinatura | Inicializa? | Parâmetros | Uso principal |
|---|---|---|---|---|
malloc |
void *malloc(size_t n) |
❌ Lixo | Total de bytes | Alocação rápida quando vai preencher imediatamente |
calloc |
void *calloc(size_t qtd, size_t tam) |
✅ Zeros | Quantidade e tamanho de cada elemento | Arrays de contadores, matrizes, flags |
realloc |
void *realloc(void *p, size_t n) |
— (preserva dados) | Ponteiro existente e novo tamanho total | Crescimento ou encolhimento dinâmico |
free |
void free(void *p) |
— | Ponteiro a liberar | Obrigatório após cada malloc/calloc |
NULL imediatamente após cada alocaçãosizeof(tipo) no cálculo do tamanho — nunca valores fixosNULL ao ponteiro após freereallocfree duas vezes no mesmo ponteirofree em qualquer caminho de saída da funçãop = realloc(p, ...) sem variável temporáriafree em ponteiro para variável local (stack)| Aspecto | C | Java |
|---|---|---|
| Alocação | Manual — malloc, calloc, realloc |
Automática — new |
| Liberação | Manual — free obrigatório |
Automática — Garbage Collector |
| Memory leak | ⚠️ Possível e silencioso | ✅ Improvável — GC libera memória inacessível |
| Dangling pointer | ⚠️ Possível — use after free |
✅ Impossível — referência nula gera exceção |
| Controle sobre desempenho | ✅ Total — você decide quando alocar e liberar | ⚠️ Parcial — GC pode pausar o programa inesperadamente |
| Array dinâmico equivalente | realloc manual |
ArrayList<Integer> automático |
int n = lerTamanho();
int *v = malloc(n * sizeof(int));
if (!v) { /* erro */ }
// ... usa v[0]..v[n-1] ...
free(v); // obrigatório!
int n = lerTamanho(); int[] v = new int[n]; // ... usa v[0]..v[n-1] ... // GC libera quando v sair de // escopo — não há free