Semana 13 — Alocação dinâmica de memória (parte 2)¶
Nesta semana
Unidade 3 · Seg 02/11 · Qua 04/11 · Sex 06/11 · ⚠️ feriado: 02/11 (Finados)
Na Semana 12 você aprendeu a pedir memória em tempo de
execução com malloc e a devolvê-la com free, criando vetores cujo tamanho só é
conhecido quando o programa roda. Esta semana damos o passo seguinte: redimensionar
um bloco já alocado com realloc, construir matrizes dinâmicas com ponteiro-para-ponteiro
(int **) e — o mais importante de tudo — aprender a gerenciar a memória sem deixar
rastros: sem vazamentos, sem ponteiros pendentes, sem liberações duplas.
Como a segunda-feira (02/11) é feriado de Finados, o estudo do conteúdo desta semana pode ser feito com calma ao longo dos primeiros dias; chegue ao encontro de quarta tendo lido e executado os exemplos. Este é um tópico difícil — talvez o mais sutil da disciplina — mas é exatamente aqui que você deixa de "usar" a memória e passa a controlá-la. Vá devagar, desenhe os ponteiros no papel e execute cada trecho.
🎯 Objetivos de aprendizagem¶
Ao final desta semana, você deverá ser capaz de:
- usar
reallocpara crescer ou encolher um bloco alocado preservando o conteúdo, e aplicar o padrão seguro com ponteiro temporário; - alocar, usar e liberar corretamente uma matriz dinâmica
linhas x colunasusandoint **(um vetor de ponteiros para as linhas); - liberar memória na ordem inversa da alocação (primeiro as linhas, depois o vetor de ponteiros) e explicar por quê;
- explicar o que é um vazamento de memória (memory leak), por que ele é um problema e
como evitá-lo com a regra "todo
malloctem seufree"; - reconhecer e evitar os erros clássicos de ponteiros: uso após
free(ponteiro pendente),freeduplo, esquecer de checarNULL, ponteiro não inicializado e perda de referência; - adotar um conjunto de boas práticas que tornam o gerenciamento de memória seguro e previsível.
📖 Segunda — Estudo do conteúdo¶
0. Retomando a Semana 12 em 90 segundos¶
Antes de avançar, relembre o essencial da alocação dinâmica:
malloc(n)pede ao sistemanbytes de memória e devolve um ponteiro para o início do bloco — ouNULLse não houver memória disponível.- Usamos
sizeofpara calcular quantos bytes queremos, e sempre verificamos o retorno. free(p)devolve ao sistema o bloco quepaponta. Todomallocprecisa de umfree.
int *v = malloc(5 * sizeof(int)); // pede espaço para 5 inteiros
if (v == NULL) { // sempre verifique!
printf("Sem memoria!\n");
return 1;
}
// ... usa v[0], v[1], ..., v[4] como um vetor normal ...
free(v); // devolve a memoria ao sistema
Tudo isso mora em <stdlib.h>. Guarde a imagem central: malloc reserva, free
devolve. Esta semana é sobre o que fazer entre os dois — e sobre nunca esquecer o
segundo.
A memória dinâmica vive no heap
As variáveis locais que você declara dentro de funções vivem na pilha (stack), vista
na Semana 04, e somem sozinhas quando a função termina. Já a
memória de malloc vive numa região chamada heap (monte) e não some sozinha: ela
fica reservada até você chamar free. É justamente por isso que gerenciá-la é
responsabilidade sua — e o tema desta semana.
1. realloc: redimensionar um bloco já alocado¶
Muitas vezes você aloca um vetor, começa a preenchê-lo e descobre que precisa de mais
espaço — ou de menos. Alocar um bloco novo, copiar tudo à mão e liberar o antigo seria
trabalhoso e fácil de errar. Para isso existe o realloc ("re-allocate", realocar):
O realloc redimensiona o bloco apontado por ptr para novo_tamanho bytes,
preservando o conteúdo que couber no novo tamanho:
- se o bloco cresce, os dados antigos são mantidos e o espaço extra fica sem inicializar (contém lixo, como qualquer memória nova);
- se o bloco encolhe, os dados até o novo tamanho são mantidos e o excedente é descartado;
- devolve um ponteiro para o bloco redimensionado — que pode ser diferente do original!
Esse último ponto é a grande sutileza. Para crescer, o sistema pode não ter espaço logo
após o bloco atual; então ele aloca um bloco novo em outro lugar, copia os dados para lá
e libera o antigo. Ou seja, o endereço pode mudar. E, como qualquer alocação,
realloc pode falhar e devolver NULL.
O erro fatal do realloc (nunca faça assim)
realloc falhar, ele devolve NULL sem liberar o bloco antigo. Ao
escrever v = realloc(v, ...), você sobrescreveu v com NULL e perdeu para
sempre o endereço do bloco antigo — que continua reservado. Isso é, ao mesmo tempo, um
vazamento de memória e a perda dos seus dados. Nunca atribua o resultado do realloc
de volta à mesma variável diretamente.
1.1 O padrão seguro do realloc¶
A forma correta usa um ponteiro temporário. Só substituímos o ponteiro original se o
realloc deu certo:
int *tmp = realloc(v, novo_tam * sizeof(int));
if (tmp != NULL) {
v = tmp; // deu certo: agora v aponta para o bloco (talvez novo)
} else {
// deu errado: v AINDA aponta para o bloco antigo, valido, intacto.
printf("Falha ao redimensionar; mantendo o vetor atual.\n");
}
Assim, se a realocação falhar, v continua válido apontando para os dados originais, e
você pode tratar o erro com calma (avisar o usuário, salvar o que já tem, encerrar de forma
limpa) em vez de perder tudo.
Curiosidade 💡 — os dois casos especiais do realloc
O realloc é um "canivete suíço" da alocação:
realloc(NULL, n)comporta-se exatamente comomalloc(n)— aloca um bloco novo.realloc(p, 0)libera o bloco (comportamento parecido comfree), embora o retorno nesse caso seja definido pela implementação; prefira sempre ofreeexplícito.
Isso permite escrever um laço que "cresce" um vetor começando de NULL, sem tratar o
primeiro caso de forma diferente — padrão que você usará no estudo dirigido de sexta.
1.2 Um exemplo completo¶
#include <stdio.h>
#include <stdlib.h>
int main(void) {
int capacidade = 2;
int *v = malloc(capacidade * sizeof(int));
if (v == NULL) { return 1; }
v[0] = 10;
v[1] = 20;
// Precisamos de espaco para 4 inteiros: crescemos o bloco.
int novo_tam = 4;
int *tmp = realloc(v, novo_tam * sizeof(int));
if (tmp == NULL) {
printf("Sem memoria para crescer.\n");
free(v); // v antigo ainda e valido; liberamos antes de sair
return 1;
}
v = tmp; // padrao seguro: so agora atualizamos v
capacidade = novo_tam;
v[2] = 30; // as duas novas posicoes estavam com lixo; preenchemos
v[3] = 40;
for (int i = 0; i < capacidade; i++) {
printf("v[%d] = %d\n", i, v[i]); // 10 20 30 40 (os antigos foram preservados)
}
free(v);
return 0;
}
Curiosidade 💡 — realloc pode ser caro
Quando o realloc precisa mover o bloco, ele copia todos os bytes para o novo local.
Se você cresce um vetor de milhões de elementos de um em um, pode acabar copiando o
conteúdo inteiro repetidas vezes — muito lento. A estratégia usual é crescer em
saltos (por exemplo, dobrando a capacidade quando ela se esgota), o que dilui o
custo das cópias. É exatamente assim que estruturas de "vetor que cresce" (as dynamic
arrays de várias linguagens) funcionam por dentro.
2. Matrizes dinâmicas com ponteiro-para-ponteiro (int **)¶
Um vetor dinâmico é um bloco linear de memória. E uma matriz (tabela com linhas e
colunas), quando as dimensões só são conhecidas em tempo de execução? A técnica clássica em
C é usar um ponteiro-para-ponteiro, int **.
A ideia tem dois níveis:
- um vetor de ponteiros (o
int **): cada posição guarda o endereço de uma linha; - para cada linha, um vetor de
intcom o número de colunas.
Visualmente, é um vetor que aponta para vários vetores:
flowchart LR
subgraph nivel1["Vetor de ponteiros (int **m)"]
L0["m[0]"]
L1["m[1]"]
L2["m[2]"]
end
subgraph nivel2["As linhas (cada uma um int* de 'colunas' elementos)"]
R0["[ m[0][0] | m[0][1] | m[0][2] | m[0][3] ]"]
R1["[ m[1][0] | m[1][1] | m[1][2] | m[1][3] ]"]
R2["[ m[2][0] | m[2][1] | m[2][2] | m[2][3] ]"]
end
L0 --> R0
L1 --> R1
L2 --> R2
Repare que, para uma matriz 3 x 4, fazemos 1 alocação para o vetor de ponteiros e
3 alocações (uma por linha) — 4 alocações no total. Guarde esse número: teremos que
fazer 4 liberações também.
2.1 Alocando a matriz¶
int linhas = 3, colunas = 4;
// 1) Aloca o vetor de ponteiros (as "linhas"):
int **m = malloc(linhas * sizeof(int *)); // atencao: sizeof(int *)!
if (m == NULL) { /* trata erro */ }
// 2) Para cada linha, aloca um vetor de 'colunas' inteiros:
for (int i = 0; i < linhas; i++) {
m[i] = malloc(colunas * sizeof(int)); // agora sizeof(int)
if (m[i] == NULL) { /* trata erro */ }
}
Duas coisas merecem sua atenção:
- No passo 1, o
sizeofé deint *(o tamanho de um ponteiro), porque cada posição demguarda um endereço, não um inteiro. - No passo 2, o
sizeofé deint, porque agora cada posição da linha guarda um inteiro de verdade.
Depois disso, você usa m[i][j] exatamente como usaria uma matriz estática — a sintaxe
de colchetes duplos funciona igualzinho.
2.2 Programa completo: alocar, usar e liberar¶
#include <stdio.h>
#include <stdlib.h>
int main(void) {
int linhas = 3, colunas = 4;
// --- ALOCACAO ---
int **m = malloc(linhas * sizeof(int *)); // vetor de ponteiros
if (m == NULL) {
printf("Sem memoria.\n");
return 1;
}
for (int i = 0; i < linhas; i++) {
m[i] = malloc(colunas * sizeof(int)); // cada linha
if (m[i] == NULL) {
printf("Sem memoria na linha %d.\n", i);
// libera o que ja foi alocado antes de sair (linhas 0..i-1):
for (int k = 0; k < i; k++) {
free(m[k]);
}
free(m);
return 1;
}
}
// --- USO: preenche m[i][j] = i * colunas + j ---
for (int i = 0; i < linhas; i++) {
for (int j = 0; j < colunas; j++) {
m[i][j] = i * colunas + j;
}
}
// --- USO: imprime a matriz ---
for (int i = 0; i < linhas; i++) {
for (int j = 0; j < colunas; j++) {
printf("%4d", m[i][j]);
}
printf("\n");
}
// --- LIBERACAO (ordem inversa da alocacao) ---
for (int i = 0; i < linhas; i++) {
free(m[i]); // 1o: libera cada linha
}
free(m); // 2o: libera o vetor de ponteiros
return 0;
}
Por que checar NULL também dentro do laço?
No exemplo acima, se a alocação de uma linha falhar no meio, precisamos liberar as linhas já alocadas e o vetor de ponteiros antes de sair — senão elas vazam. Em programas didáticos pequenos você pode simplificar, mas é importante ver o padrão correto: o tratamento de erro também precisa limpar a bagunça parcial.
3. Liberar na ORDEM CORRETA (a inversa da alocação)¶
Este ponto é tão importante que merece seção própria. Ao liberar a matriz, a ordem é obrigatoriamente:
- primeiro,
freede cada linha (m[0],m[1], ...); - depois,
freedo vetor de ponteiros (m).
Nunca libere m antes das linhas!
Se você fizer free(m) primeiro, o vetor de ponteiros deixa de existir — e com ele
os endereços m[0], m[1], ... Você perdeu os endereços das linhas e não tem mais
como liberá-las: elas vazam. Pior: tentar acessar m[i] depois de free(m) é uso
após free (veja a seção 5). A regra é simples e mecânica: libere na ordem inversa
da que você alocou. Você alocou o vetor de ponteiros primeiro e as linhas depois; então
libere as linhas primeiro e o vetor de ponteiros por último.
Pense na matriz como uma árvore: m é o tronco, m[i] são os galhos. Para desmontar
sem perder nada, corte os galhos antes do tronco.
4. Vazamentos de memória (memory leaks)¶
Um vazamento de memória acontece quando você aloca um bloco e nunca o libera — e, além disso, perde o ponteiro que apontava para ele. A memória fica reservada, mas inacessível: o programa não pode mais usá-la nem devolvê-la.
void faz_algo(void) {
int *v = malloc(1000 * sizeof(int));
// ... usa v ...
// ESQUECEU o free(v)!
} // ao sair da funcao, a variavel local 'v' morre.
// O BLOCO de 1000 inteiros continua reservado, mas ninguem mais tem seu endereco.
Quando faz_algo termina, a variável local v (que estava na pilha) desaparece — mas o
bloco de 1000 inteiros continua ocupando o heap, agora sem nenhum ponteiro que chegue
até ele. Vazou.
Por que isso é um problema? Um vazamento pontual, num programa que roda por 1 segundo, raramente causa dano visível — o sistema operacional recupera toda a memória do processo quando ele termina. O problema aparece em programas que rodam por muito tempo (um servidor, um jogo, um serviço) e vazam repetidamente, por exemplo dentro de um laço ou a cada requisição: o consumo de memória cresce sem parar até o programa ficar lento, travar ou ser encerrado pelo sistema. É o clássico "por que meu programa está comendo 4 GB de RAM depois de 3 horas?".
Como evitar? A regra de ouro, que você já viu na Semana 12, é:
A regra de ouro do gerenciamento de memória
Todo malloc (ou calloc, ou realloc) tem, em algum lugar, o seu free
correspondente.
Ao escrever um malloc, pergunte-se imediatamente: "quem, e quando, vai liberar
isto?". Se você não sabe responder, ainda não terminou de projetar o código.
Curiosidade 💡 — a Valgrind, sua detetive de memória
Como saber se o seu programa vaza? No Linux, a ferramenta Valgrind executa seu programa e rastreia cada alocação e liberação. Ao final, ela relata quantos bytes ficaram "definitely lost" (vazaram), aponta em qual linha foram alocados e ainda detecta usos inválidos (ler além do bloco, usar memória já liberada, etc.). Roda-se assim:
A linha mágica que você quer ver é: "All heap blocks were freed -- no leaks are possible". Vale ligar a Valgrind nos seus exercícios desta semana — ela transforma um tópico abstrato em algo concreto e verificável.5. Erros clássicos de ponteiros e alocação¶
Esta é a "galeria de armadilhas" da semana. Reconhecê-las é metade do caminho para evitá-las.
5.1 Uso após free (ponteiro pendente / dangling pointer)¶
Depois de free(p), o bloco não é mais seu — mas a variável p ainda contém o
endereço antigo. Um ponteiro nessa situação é chamado de ponteiro pendente (dangling
pointer). Usá-lo é erro grave:
int *p = malloc(sizeof(int));
*p = 42;
free(p); // devolvemos o bloco ao sistema
printf("%d\n", *p); // ERRO: uso apos free! p ainda aponta para memoria que nao e mais nossa
Curiosidade 💡 — por que 'usar após free' é tão perigoso
O pior de tudo é que muitas vezes o programa não trava na hora — o valor antigo ainda
pode estar lá "por sorte", e o programa parece funcionar. Mas, assim que o sistema
reaproveitar aquele bloco para uma outra alocação, *p passará a ler (ou pior,
escrever) sobre dados que agora pertencem a outra parte do programa. O resultado são
bugs intermitentes e não reproduzíveis — os mais difíceis de caçar que existem. Em
software real, esse tipo de falha (use-after-free) é, inclusive, uma das principais
portas de entrada para falhas de segurança.
A defesa: logo após free(p), faça p = NULL;. Um ponteiro NULL usado
indevidamente faz o programa falhar na hora (de forma óbvia), em vez de silenciosamente
corromper dados.
5.2 free duplo (double free)¶
Liberar duas vezes o mesmo bloco é comportamento indefinido — costuma corromper as estruturas internas do gerenciador de memória e travar o programa:
A boa notícia: a mesma defesa da seção anterior resolve. Como free(NULL) é seguro (não
faz absolutamente nada), atribuir p = NULL após o primeiro free neutraliza um
eventual segundo free:
5.3 Esquecer de verificar NULL¶
malloc e realloc podem falhar e devolver NULL. Se você usar o ponteiro sem checar,
qualquer acesso p[0] será uma desreferência de NULL — falha imediata (o famoso
segmentation fault):
Sempre: alocou, cheque.
5.4 Usar ponteiro não inicializado¶
Um ponteiro declarado mas não inicializado contém lixo — um endereço qualquer. Usá-lo é escrever/ler num lugar aleatório da memória:
int *p; // p contem LIXO (um endereco imprevisivel)
*p = 10; // ERRO: escreve num lugar desconhecido -> comportamento indefinido
A defesa é inicializar sempre, nem que seja com NULL:
5.5 Perder a referência (sobrescrever o ponteiro antes de liberar)¶
Se você reatribui um ponteiro sem ter liberado o bloco anterior, perde o endereço do bloco antigo — que vaza:
int *p = malloc(100 * sizeof(int));
p = malloc(200 * sizeof(int)); // ERRO: o primeiro bloco (100 ints) vazou!
O correto é liberar antes de reapontar (free(p); e só então o novo malloc) — ou, no caso
de redimensionar, usar realloc com o padrão seguro da seção 1.
6. Boas práticas (o resumo que salva você)¶
Reunindo tudo, um punhado de hábitos elimina a grande maioria dos bugs de memória:
| Prática | Por quê |
|---|---|
Inicialize ponteiros (int *p = NULL;) |
Evita usar lixo; NULL é um valor seguro e detectável. |
Verifique o retorno de malloc/realloc |
Eles podem falhar; usar NULL trava o programa. |
Todo malloc ↔ free |
Sem par, é vazamento certo. Planeje o free ao escrever o malloc. |
p = NULL após free |
Neutraliza uso-após-free e free duplo de uma só vez. |
Padrão seguro do realloc (via tmp) |
Preserva os dados se a realocação falhar. |
| Libere na ordem inversa da alocação | Em estruturas aninhadas (matrizes), evita perder referências. |
Um esqueleto mental para qualquer código com memória dinâmica
Decore este ciclo de seis passos. Ele cabe em qualquer situação — de um vetor simples à linha de uma matriz.7. Erros comuns desta semana (resumão)¶
Checklist de armadilhas
-
v = realloc(v, ...)direto (semtmp) — vaza e perde os dados se falhar. -
sizeof(int)onde deveria sersizeof(int *)ao alocar o vetor de ponteiros. - Liberar
mantes das linhas — as linhas vazam. - Esquecer o
free— vazamento de memória. - Usar
pdepois defree(p)— ponteiro pendente. -
free(p)duas vezes — corrompe o heap (defesa:p = NULLapós ofree). - Não checar
NULLapósmalloc/realloc. - Usar ponteiro não inicializado (contém lixo).
- Reatribuir um ponteiro sem liberar o bloco anterior — perda de referência.
👥 Quarta — Encontro¶
Chegue ao encontro tendo lido o material acima e tendo executado ao menos os
exemplos crescer_vetor.c e matriz_dinamica.c. Como a segunda foi feriado, capriche na
preparação nos dias anteriores. Roteiro previsto:
- Revisão relâmpago:
realloce o padrão seguro; a estrutura de uma matrizint **; a ordem de liberação. - Desenho ao vivo: vamos desenhar, no quadro, o vetor de ponteiros e as linhas, e
acompanhar o que acontece na memória a cada
malloce a cadafree. - Galeria de erros com a Valgrind: provocamos de propósito um vazamento e um
uso-após-
free, e usamos a Valgrind para ver o problema aparecer no relatório.
Problema-guia do encontro — vetor dinâmico que cresce sob demanda: ler inteiros do
usuário até que ele digite 0 (sentinela), guardando todos em um vetor que cresce
com realloc conforme necessário, e ao final imprimir os valores e a soma. Este é o
esqueleto do "vetor que cresce" que aparece por toda parte na programação.
#include <stdio.h>
#include <stdlib.h>
int main(void) {
int *v = NULL; // comeca vazio (realloc(NULL, ...) == malloc)
int tamanho = 0; // quantos elementos ja guardamos
int capacidade = 0; // quantos cabem no bloco atual
int x;
printf("Digite inteiros (0 para terminar):\n");
while (scanf("%d", &x) == 1 && x != 0) {
if (tamanho == capacidade) { // encheu? precisa crescer
int nova_cap = (capacidade == 0) ? 1 : capacidade * 2; // dobra
int *tmp = realloc(v, nova_cap * sizeof(int));
if (tmp == NULL) { // padrao seguro
printf("Sem memoria; parando a leitura.\n");
break;
}
v = tmp;
capacidade = nova_cap;
}
v[tamanho] = x;
tamanho++;
}
long soma = 0;
printf("Valores lidos (%d): ", tamanho);
for (int i = 0; i < tamanho; i++) {
printf("%d ", v[i]);
soma += v[i];
}
printf("\nSoma = %ld\n", soma);
free(v); // um unico free para todo o vetor (mesmo tendo crescido varias vezes)
return 0;
}
Perguntas para pensar antes do encontro: por que começar com v = NULL e capacidade = 0
funciona sem um caso especial para o primeiro elemento? Por que dobrar a capacidade,
em vez de crescer de um em um? Quantas vezes o realloc é chamado ao ler 100 números?
✍️ Sexta — Estudo dirigido¶
Este é um tema pouco 'testado' no Beecrowd
Alocação dinâmica é difícil de avaliar em juízes online automáticos (que olham só a saída e não enxergam vazamentos). Por isso, esta semana o foco principal são os desafios do zero — onde você constrói e, sobretudo, libera a memória corretamente. Ainda assim, vale revisitar problemas de vetores e matrizes resolvendo-os com memória dinâmica e conferindo com a Valgrind.
Exercícios guiados (Beecrowd)¶
Reforço opcional: refaça, agora com malloc/realloc/free, problemas de vetores/matrizes
já conhecidos.
| Nº | Problema | O que exercita |
|---|---|---|
| 1180 | Menor e Posição | vetor dinâmico do tamanho lido |
| 1181 | Linha na Matriz | leitura por linha (base para int **) |
| 1182 | Coluna na Matriz | percorrer colunas de uma matriz dinâmica |
| 1435 | Square Matrix I | montar/imprimir matriz n x n dinâmica |
Para todos: aloque com base no tamanho lido, use e libere tudo ao final. Rode com
valgrind --leak-check=full.
Desafios para escrever do zero¶
- Vetor que cresce com
realloc: parta dovetor_sob_demanda.cdo encontro. Leia valores até uma sentinela (por exemplo, um número negativo), guardando todos com crescimento porrealloc. Ao final, imprima o maior, o menor e a média. - Matriz dinâmica
N x Mdo usuário: leiaNeM, aloque uma matrizint **, leia osN * Mvalores, imprima a soma de cada linha e a soma de cada coluna, e libere tudo na ordem correta. - Função que dobra um vetor: escreva
int *dobra_capacidade(int *v, int *capacidade)que duplica o tamanho do bloco apontado porv(usando o padrão seguro dorealloc), atualiza*capacidadee devolve o novo ponteiro. Teste-a chamando-a algumas vezes e imprimindo o conteúdo preservado. - Caça-vazamentos: dado o trecho abaixo, encontre e corrija todos os problemas de memória (há mais de um!). Depois confirme com a Valgrind que não sobrou nenhum vazamento.
Para investigar (curiosidade prática)¶
- Uso após
free, ao vivo: aloque umint, atribua*p = 123, dêfree(p)e imprima*plogo em seguida. Rode algumas vezes. O valor "sobrevive"? Agora faça outra alocação entre ofreee oprintfe observe. Explique com suas palavras por que isso é perigoso, ligando com a curiosidade da seção 5.1. - Testando o padrão seguro: modifique o
reallocpara pedir um tamanho absurdamente grande (por exemplo,realloc(v, 1000000000000UL * sizeof(int))) e confirme que o padrão comtmpfaz o programa avisar o erro sem perder os dados quevjá tinha.
✅ Checklist de autoavaliação¶
Marque com sinceridade — se algum item não estiver ✅, volte à seção correspondente.
- Sei usar
reallocpara crescer/encolher um bloco preservando o conteúdo. - Uso o padrão seguro do
realloc(ponteirotmp) e explico por que ele importa. - Aloco uma matriz
int **comsizeof(int *)no vetor de ponteiros esizeof(int)nas linhas. - Libero a matriz na ordem correta: primeiro as linhas, depois o vetor de ponteiros.
- Explico o que é um vazamento de memória, por que é problema e como evitá-lo.
- Reconheço e evito: uso após
free,freeduplo,NULLnão checado, ponteiro não inicializado e perda de referência. - Aplico as boas práticas (inicializar, checar, parear
malloc/free,p = NULLapósfree).
🔗 Referências e para se aprofundar¶
- Material 2026.1 — Aula 11: Ponteiros e Alocação Dinâmica.
- BACKES, André. Linguagem C: completa e descomplicada (alocação dinâmica:
malloc,calloc,realloc,free; ponteiros e matrizes dinâmicas). - KERNIGHAN, B.; RITCHIE, D. The C Programming Language (o clássico "K&R"), cap. 5
(ponteiros, arrays e alocação com
malloc/free). - Referência: cppreference —
realloce cppreference —free. - Prática: Beecrowd.
Antes de seguir para a Semana 14
Você agora sabe reservar, redimensionar e devolver memória com segurança — a base de
toda estrutura de dados séria. Na Semana 14 vamos dar
nome e forma aos dados: os registros (struct), que agrupam vários campos de
tipos diferentes sob um mesmo nome, e as enumerações (enum). Junte isso ao que você
aprendeu sobre ponteiros e alocação e você terá tudo para construir suas próprias
estruturas dinâmicas — o grande salto que fecha a Unidade 3. Antes de avançar, garanta
que seus programas desta semana passam limpos na Valgrind: memória bem gerenciada é
um hábito, e o momento de formá-lo é agora.