Semana 12 — Alocação dinâmica de memória (parte 1)¶
Nesta semana
Unidade 3 · Seg 26/10 · Qua 28/10 · Sex 30/10 · ⚠️ 28/10 (Dia do Servidor Público) pode afetar o encontro de quarta — ver plano de curso
Até agora, todo vetor que você declarou tinha um tamanho fixo, decidido por você
enquanto escrevia o código (int v[100];). Mas e quando o tamanho só é conhecido
durante a execução — quando é o usuário quem decide quantos elementos vai
querer? Esta semana resolve esse problema com a alocação dinâmica de memória:
pedir ao sistema, em tempo de execução, um bloco de memória do tamanho exato de que
precisamos, usá-lo como um vetor e, ao terminar, devolvê-lo.
Este é o encontro natural de tudo o que você viu na Unidade 3: endereços, ponteiros e a divisão da memória em pilha e heap (Semana 10). Aqui os ponteiros deixam de ser um conceito abstrato e passam a fazer um trabalho concreto e indispensável. É um tópico difícil — leia com calma, desenhe a memória no papel e, principalmente, digite e execute cada exemplo.
🎯 Objetivos de aprendizagem¶
Ao final desta semana, você deverá ser capaz de:
- explicar a diferença entre memória estática (tamanho fixo, decidido em compilação) e memória dinâmica (tamanho decidido em execução), relacionando-as com a pilha e o heap;
- usar o operador
sizeofpara calcular corretamente o tamanho, em bytes, de um bloco a alocar; - alocar memória no heap com
malloc, entendendo que ela devolve um ponteiro para o bloco reservado; - verificar sempre se a alocação teve sucesso (teste de
NULL) antes de usar a memória; - usar a memória alocada exatamente como um vetor (
v[i]); - liberar a memória com
freequando ela não for mais necessária, e entender por que todomallocprecisa do seufree; - descrever, em linhas gerais, o que é um vazamento de memória e por que
callocpode ser conveniente.
📖 Segunda — Estudo do conteúdo¶
1. O problema: tamanho fixo × tamanho variável¶
Volte à ideia de vetor da Unidade 2. Para guardar as notas de uma turma, você escrevia algo assim:
Isso funciona, mas esconde dois incômodos:
- E se a turma tiver mais de 100 alunos? O programa não tem para onde crescer — os
índices
notas[100],notas[101]... invadem memória que não é sua (comportamento indefinido, provável travamento). - E se a turma tiver 5 alunos? Você reservou 100 posições e usa 5. As outras 95 ficaram desperdiçadas.
O ponto central é este: int notas[100]; fixa o tamanho em tempo de compilação — ou
seja, o número 100 precisa ser conhecido enquanto você escreve o programa, muito
antes de ele rodar. Mas, na vida real, quem conhece o tamanho é o usuário, e só em
tempo de execução:
int n;
printf("Quantos alunos? ");
scanf("%d", &n);
int notas[n]; // <- e agora? n só foi conhecido AGORA, em execução
Esse int notas[n]; (chamado VLA, vetor de tamanho variável) existe em C moderno, mas
tem limitações sérias: o vetor vai para a pilha (que é pequena e some quando a função
termina), não pode ser redimensionado e nem toda situação o permite. A solução geral e
profissional é outra: pedir a memória ao sistema, na quantidade certa, usando a
alocação dinâmica.
1.1 Onde essa memória vive: pilha × heap¶
Na Semana 10 vimos que a memória de um programa em execução se organiza em regiões. Duas nos interessam agora:
| Região | O que guarda | Tamanho decidido | Tempo de vida | Quem gerencia |
|---|---|---|---|---|
| Pilha (stack) | variáveis locais e parâmetros; vetores de tamanho fixo | em compilação | some quando a função termina | o compilador, automaticamente |
| Heap (monte) | memória alocada dinamicamente | em execução | até você liberar com free |
você, manualmente |
flowchart TB
subgraph mem["Memória do programa em execução"]
direction TB
S["PILHA (stack)<br/>variaveis locais, parametros<br/>tamanho fixo, vida curta<br/>gerenciada pelo compilador"]
H["HEAP (monte)<br/>blocos de malloc<br/>tamanho decidido em execucao<br/>voce libera com free"]
end
A alocação dinâmica sempre acontece no heap. É isso que lhe dá dois superpoderes que a pilha não tem: o tamanho pode ser decidido em execução e o bloco sobrevive ao fim da função que o criou (contanto que você guarde o ponteiro para ele). Em troca, você assume uma responsabilidade: devolver a memória quando terminar.
Curiosidade 💡 — por que o heap "sobrevive" e a pilha não?
A pilha funciona como uma pilha de pratos: a cada chamada de função, empilha-se um "quadro" com suas variáveis locais; quando a função retorna, o quadro inteiro é desempilhado e aquele espaço é reaproveitado na hora. Por isso uma variável local não pode ser devolvida por endereço — o espaço dela já foi reciclado assim que a função acabou.
O heap é diferente: é uma área "livre" que o programa vai loteando sob demanda. Um
bloco alocado com malloc fica lá até você mandar liberá-lo com free, mesmo que a
função que o criou já tenha terminado. É exatamente por isso que uma função consegue
alocar um vetor no heap e devolver um ponteiro para ele (veremos isso na sexta) —
algo impossível com um vetor da pilha.
2. sizeof: medindo os tipos antes de alocar¶
Para pedir memória, precisamos dizer quantos bytes queremos. E, para isso, precisamos
saber quanto ocupa cada tipo. O operador sizeof (que você já conheceu lá na
Semana 01) responde exatamente a essa pergunta, devolvendo o
tamanho em bytes de um tipo ou de uma variável:
printf("um int ocupa %zu bytes\n", sizeof(int)); // tipicamente 4
printf("um double ocupa %zu bytes\n", sizeof(double)); // tipicamente 8
printf("um char ocupa %zu bytes\n", sizeof(char)); // sempre 1
(Lembre: o especificador correto para o resultado de sizeof é %zu.)
Por que isso importa agora? Porque um vetor de n inteiros ocupa n * sizeof(int)
bytes. Se cada int tem 4 bytes, um vetor de 10 inteiros ocupa 10 * 4 = 40 bytes. É
essa conta que passaremos para o malloc.
Use sizeof, não o número "cru"
Você poderia escrever malloc(n * 4) chutando que int tem 4 bytes. Não faça
isso. O tamanho de um int pode variar entre sistemas, e o dia em que você trocar
o tipo (de int para double, por exemplo) esse 4 vira um bug silencioso. Escrever
n * sizeof(int) é sempre correto e deixa clara a intenção. Regra de ouro:
deixe o compilador calcular os tamanhos para você.
3. malloc: pedindo memória ao sistema¶
A função malloc (memory allocation) está na biblioteca <stdlib.h>. Ela faz uma
coisa só, mas fundamental: reserva um bloco contíguo de memória no heap com o tamanho
que você pediu e devolve um ponteiro para o início desse bloco.
Vamos dissecar essa linha, que é o coração da semana:
n * sizeof(int)— o tamanho em bytes que queremos: espaço paraninteiros.malloc(...)— pede esse bloco ao sistema e devolve o endereço do começo dele.int *v =— guardamos esse endereço num ponteiro paraint. A partir daqui,vaponta para o primeiro dosninteiros reservados.
flowchart LR
P["v (ponteiro na pilha)"] -->|aponta para| B
subgraph heap["Bloco no HEAP: n * sizeof(int) bytes"]
direction LR
B["v[0]"] --- C["v[1]"] --- D["v[2]"] --- E["..."] --- F["v[n-1]"]
end
Repare na divisão de trabalho: o ponteiro v é uma variável local pequena, que mora na
pilha; o bloco de n inteiros que ele aponta mora no heap. O ponteiro é apenas
o "endereço na etiqueta"; a memória de verdade está do outro lado da seta.
Curiosidade 💡 — void *: o ponteiro "genérico"
Tecnicamente, malloc devolve um void * — um ponteiro "sem tipo", que apenas diz
"o bloco começa aqui" sem afirmar o que há dentro. Em C, um void * é convertido
automaticamente para o tipo de ponteiro do lado esquerdo (int *, double *,
etc.), então a atribuição int *v = malloc(...) simplesmente funciona. Faz sentido:
malloc só sabe quantos bytes você pediu, não se são inteiros, reais ou
caracteres — quem dá esse significado aos bytes é você, ao guardar o retorno num
ponteiro de um tipo específico.
Fazer cast do malloc? Em C, prefira não fazer
Você verá, em muitos códigos antigos, o retorno do malloc sendo convertido
explicitamente:
(int *) é desnecessário (a conversão de void * é automática) e
pode até mascarar um erro — o de esquecer o #include <stdlib.h>. Por isso, o
estilo idiomático em C é escrever sem o cast:
(O cast é obrigatório em C++, o que costuma ser a origem da confusão. Na nossa
disciplina, que é C, escreva sem cast.)
4. Sempre verifique se deu certo: o teste de NULL¶
O sistema tem uma quantidade finita de memória. Se você pedir mais do que há
disponível, malloc não consegue atender e, em vez de um endereço válido, devolve o
ponteiro especial NULL (o "endereço nenhum", que vale 0). Usar um ponteiro NULL
como se fosse memória de verdade trava o programa (o famoso segmentation fault).
Por isso, toda chamada de malloc deve ser seguida de uma verificação:
int *v = malloc(n * sizeof(int));
if (v == NULL) { // a alocacao falhou?
printf("Erro: memoria insuficiente.\n");
return 1; // encerra sinalizando erro ao sistema
}
// daqui para baixo, v aponta para memoria valida com seguranca
Nunca confie que o malloc funcionou — teste NULL sempre
Pular o teste de NULL é um dos erros mais perigosos com alocação dinâmica, porque o
programa parece funcionar... até o dia em que a memória acaba (um n gigante, um
laço que aloca demais) e ele trava de forma inexplicável para quem não testou. Adote o
hábito: malloc seguido, na linha de baixo, do if (p == NULL). Sempre.
Curiosidade 💡 — quando o malloc realmente falha?
Em programas pequenos de disciplina, malloc quase nunca falha — há memória de sobra.
Mas a verificação não é "frescura": em sistemas reais (servidores, dispositivos
embarcados, programas que processam arquivos enormes) a memória acaba de verdade, e
um programa robusto precisa reagir com elegância em vez de simplesmente morrer. Aprender
a testar NULL desde já é aprender a escrever código que não quebra na hora errada.
5. Usando a memória: é um vetor como qualquer outro¶
Aqui vem a parte tranquilizadora. Depois de alocado e verificado, o bloco se comporta exatamente como um vetor comum. Você usa a mesma notação de colchetes:
for (int i = 0; i < n; i++) {
v[i] = i * i; // escreve como em qualquer vetor
}
printf("%d\n", v[0]); // le como em qualquer vetor
Isso não é coincidência: em C, o nome de um vetor é, essencialmente, um ponteiro para o
seu primeiro elemento, e v[i] é apenas uma forma legível de dizer "o elemento a i
posições depois de onde v aponta". Ou seja, um vetor estático e um bloco de malloc são,
para efeito de uso, a mesma coisa — a diferença está em onde vivem (pilha × heap) e
em quem os cria e destrói.
6. free: devolvendo a memória¶
Como o heap é gerenciado por você, é você quem precisa devolver a memória quando
não precisar mais dela. Isso se faz com free, passando o ponteiro que o malloc
devolveu:
free(v); // devolve o bloco ao sistema; ele pode ser reaproveitado
v = NULL; // boa pratica: o ponteiro nao aponta mais para nada valido
A regra mental é simples e inegociável:
Todo
mallocdeve ter, em algum momento, o seufreecorrespondente.
Depois do free, o bloco não é mais seu — usá-lo é um erro grave. Colocar v = NULL
logo após o free é uma proteção barata: se, por acidente, alguém tentar usar v
depois, ele estará em NULL (o que é fácil de detectar) em vez de apontar para memória já
liberada (o que causa bugs traiçoeiros, chamados dangling pointer — ponteiro
"pendurado").
O que free não faz
free(v)não apaga a variávelvnem a zera — apenas devolve o bloco para onde ela aponta.vcontinua com o mesmo endereço (agora inválido); por isso ov = NULLlogo em seguida.- Não use a memória depois do
free(use-after-free), nem chamefreeduas vezes no mesmo ponteiro (double free). Ambos são erros sérios que aprofundaremos na Semana 13. freeespera exatamente o ponteiro que omallocdevolveu. Não passe um ponteiro "no meio" do bloco (ex.:free(v + 2)).
Curiosidade 💡 — e se eu esquecer o free? (vazamento de memória)
Se você aloca memória e nunca a libera, aquele bloco fica reservado até o programa terminar, mesmo sem ninguém mais usá-lo. Isso é um vazamento de memória (memory leak). Num programa que roda e fecha logo, o sistema recupera tudo no fim e "ninguém percebe". Mas num programa que fica horas ou dias rodando (um servidor, um jogo, um app) e que vaza um pouquinho a cada operação, o consumo de memória só cresce até o programa engasgar. Vamos investigar esse efeito na sexta e estudá-lo a fundo na Semana 13. Por ora, grave o par: alocou, use; terminou, libere.
7. Um exemplo completo, do início ao fim¶
Vamos juntar tudo num programa que faz o que motivou a semana: perguntar ao usuário quantos valores ele quer, alocar um vetor exatamente desse tamanho, preenchê-lo, calcular a soma e a média e, ao final, liberar a memória.
#include <stdio.h>
#include <stdlib.h> // malloc, free
int main(void) {
int n;
printf("Quantos numeros voce vai digitar? ");
scanf("%d", &n);
if (n <= 0) {
printf("Quantidade invalida.\n");
return 1;
}
// 1) ALOCA um vetor dinamico de n inteiros no heap
int *v = malloc(n * sizeof(int));
// 2) VERIFICA se a alocacao teve sucesso
if (v == NULL) {
printf("Erro: memoria insuficiente.\n");
return 1;
}
// 3) USA como um vetor normal: preenche
for (int i = 0; i < n; i++) {
printf("Valor %d: ", i + 1);
scanf("%d", &v[i]);
}
// 4) PROCESSA: soma e media
long soma = 0; // long para nao estourar com muitos valores
for (int i = 0; i < n; i++) {
soma += v[i];
}
double media = (double) soma / n; // cast para nao cair na divisao inteira
printf("Soma: %ld\n", soma);
printf("Media: %.2f\n", media);
// 5) LIBERA a memoria e anula o ponteiro
free(v);
v = NULL;
return 0;
}
Percorra o programa notando os cinco passos — alocar, verificar, usar, processar, liberar. Eles são o "esqueleto" de praticamente todo uso de alocação dinâmica; vale memorizá-los nessa ordem.
Repare no (double) soma / n
A média mistura um inteiro (soma) com outro (n). Sem o cast, soma / n seria uma
divisão inteira (a armadilha da Semana 01!) e a média
sairia truncada. O (double) força a conta a ser feita em ponto flutuante. Alocação
dinâmica é novidade, mas as armadilhas antigas continuam valendo.
8. calloc: alocar e zerar de uma vez¶
Existe uma prima próxima do malloc que vale conhecer: a função calloc (cleared
allocation). Ela também reserva memória no heap, mas com duas diferenças:
- Recebe dois argumentos: a quantidade de elementos e o tamanho de cada um.
- Zera todos os bytes do bloco antes de devolvê-lo.
int *a = malloc(n * sizeof(int)); // conteudo INDEFINIDO (lixo de memoria)
int *b = calloc(n, sizeof(int)); // conteudo garantidamente ZERADO
A diferença essencial está no conteúdo inicial. O bloco do malloc vem com lixo (o
que estava naquela região da memória) — exatamente como uma variável local não inicializada
(lembra do "lixo de memória" da Semana 01?). Já o calloc garante que tudo começa em 0.
| Aspecto | malloc(n * sizeof(int)) |
calloc(n, sizeof(int)) |
|---|---|---|
| Argumentos | 1 (total de bytes) | 2 (nº de elementos, tamanho de cada) |
| Conteúdo inicial | indefinido (lixo) | tudo zero |
| Velocidade | levemente mais rápido | um pouco mais lento (zera) |
| Quando usar | vou preencher tudo mesmo | quero começar zerado (ex.: contadores) |
Tanto malloc quanto calloc se liberam com o mesmo free.
Alocar não é inicializar (no caso do malloc)
Um erro comum é alocar com malloc e sair somando em v[i] sem antes atribuir valor a
cada posição — os valores iniciais são lixo, e o resultado será imprevisível. Ou
você preenche todas as posições antes de lê-las (como no exemplo da seção 7), ou usa
calloc para começar tudo em zero. Nunca leia uma posição de malloc que você ainda
não escreveu.
9. Erros comuns desta semana (resumão)¶
Checklist de armadilhas
- Esquecer o
#include <stdlib.h>(onde vivemmalloc,callocefree). - Não testar
NULLdepois domalloc/calloc. - Errar a conta do tamanho: usar
malloc(n)em vez demalloc(n * sizeof(int)). - Ler posições de um bloco de
mallocantes de preenchê-las (contêm lixo). - Esquecer o
free(vazamento de memória). - Usar a memória depois do
free(use-after-free) ou darfreeduas vezes. - Fazer cast desnecessário do
mallocem C (e, pior, esquecer o<stdlib.h>). - Confundir o ponteiro (mora na pilha) com o bloco apontado (mora no heap).
👥 Quarta — Encontro¶
Chegue ao encontro tendo estudado o material acima e tendo executado o exemplo
media_dinamica.c. O encontro é para consolidar e tirar dúvidas — não para ver o conteúdo
pela primeira vez. Roteiro previsto:
- Revisão relâmpago: pilha × heap, o papel do ponteiro, e o "esqueleto" dos cinco passos (alocar → verificar → usar → processar → liberar).
- Construção ao vivo do problema-guia abaixo, desenhando a memória no quadro para separar o ponteiro (pilha) do bloco (heap).
- Galeria de erros: vamos provocar de propósito os erros da seção 9 — esquecer o
sizeof, pular o teste deNULL, usar depois dofree— para você aprender a reconhecê-los.
Sobre o encontro de quarta (28/10)
O calendário do servidor público às vezes marca recesso nesta data. Se o encontro de quarta for afetado, siga a orientação do professor (remarcação ou gravação). O conteúdo e os prazos da semana permanecem.
Problema-guia do encontro — o vetor que cresce sob demanda: ler n do usuário, alocar
dinamicamente um vetor de n reais, ler os valores e reportar quantos estão acima da
média. É um problema que exige dois passos sobre os dados (primeiro calcular a média,
depois contar), o que só é possível porque guardamos todos os valores — e para isso
precisamos de um vetor do tamanho certo, decidido em execução.
#include <stdio.h>
#include <stdlib.h>
int main(void) {
int n;
printf("Quantos valores? ");
scanf("%d", &n);
if (n <= 0) {
printf("Quantidade invalida.\n");
return 1;
}
double *v = malloc(n * sizeof(double)); // vetor dinamico de double
if (v == NULL) {
printf("Erro: memoria insuficiente.\n");
return 1;
}
// Leitura (lembre: double no scanf usa %lf)
double soma = 0.0;
for (int i = 0; i < n; i++) {
printf("Valor %d: ", i + 1);
scanf("%lf", &v[i]);
soma += v[i];
}
double media = soma / n;
// Segunda passada: contar quantos superam a media
int acima = 0;
for (int i = 0; i < n; i++) {
if (v[i] > media) {
acima++;
}
}
printf("Media: %.2f\n", media);
printf("%d valor(es) acima da media.\n", acima);
free(v);
v = NULL;
return 0;
}
Perguntas para pensar antes do encontro: por que precisamos guardar os valores num
vetor, em vez de só ir somando? (Dica: para comparar com a média, precisamos dos valores
depois de a média estar pronta.) E por que soma aqui é double, e não long como no
exemplo da segunda?
✍️ Sexta — Estudo dirigido¶
Sobre o Beecrowd nesta semana
Alocação dinâmica não é o foco dos problemas iniciais do Beecrowd — a maioria deles
aceita vetores de tamanho fixo tranquilamente. Por isso, o exercício desta sexta é
diferente: pegue problemas de vetores que você já sabe resolver e refaça-os usando
alocação dinâmica, lendo o tamanho n do usuário (ou da entrada) e alocando o vetor
com malloc. O objetivo é fixar o "esqueleto" dos cinco passos, não vencer um problema
novo. Sugestões de bons candidatos para essa releitura:
| Nº | Problema | Como aplicar alocação dinâmica |
|---|---|---|
| 1005 / 1006 | Médias | aloque o vetor de notas com malloc (mesmo sendo poucas) |
| 1010 | Cálculo Simples | pratique alocar e liberar mesmo em casos pequenos |
| 1172 | Vetor de Trabalho | leia o tamanho e aloque dinamicamente |
| 1173 | Preenchendo Vetor I | aloque n posições e preencha num laço |
| 1174 | Vetor de Verdade | leia, aloque, filtre e imprima |
| 1180 | Menor e Posição | aloque o vetor, ache o menor e a posição, depois free |
O código passa (o Beecrowd só olha a saída), mas o treino de alocar/verificar/liberar é seu.
Desafios para escrever do zero¶
- Estatísticas de um vetor dinâmico: ler
n, alocar um vetor deninteiros, preencher e imprimir soma, média, maior e menor. Não esqueça ofreeno final. - Nomes/valores sob demanda: ler
ne, num laço, lernvalores reais (double, com%lf) para um vetor alocado dinamicamente; ao final, imprimir a lista na ordem inversa da leitura. - Cópia para um novo vetor dinâmico: dado um vetor
vde tamanhon(pode ser fixo), alocar um segundo vetorw, também dinâmico, do mesmo tamanho, e copiar todos os elementos devparaw. Ao terminar, liberew. - Função que devolve um ponteiro (importante!): escrever uma função com a assinatura
que aloca internamente um vetor de n inteiros, preenche cada posição com
i * i e devolve o ponteiro para quem chamou. Na main, use o retorno como um vetor
normal e, ao final, dê free. Reflita: por que isso funciona com o heap, mas
não funcionaria se a função declarasse um vetor local int v[n]; e tentasse
devolvê-lo? (Volte à curiosidade da seção 1.1.)
Para investigar (curiosidade prática)¶
- Provocando um vazamento: escreva um laço que executa muitas vezes (ex.: um milhão) e,
a cada volta, faz
mallocde um bloco grande sem ofreecorrespondente. Rode e observe (no Gerenciador de Tarefas /htop/top) o consumo de memória do programa subir sem parar. Depois adicione ofreedentro do laço e compare. O que muda? - Esquecendo o teste de
NULL: peça umnabsurdamente grande (algo que o sistema não consiga alocar) e tente usarv[0]sem ter testadoNULL. Observe o programa travar (segmentation fault). Depois, coloque o teste de volta e veja o programa falhar com elegância, imprimindo uma mensagem de erro em vez de morrer. Discuta: por que a segunda versão é preferível num programa de verdade?
✅ Checklist de autoavaliação¶
Marque com sinceridade — se algum item não estiver ✅, volte à seção correspondente.
- Explico a diferença entre tamanho fixo em compilação e decidido em execução, e por que a segunda situação pede alocação dinâmica.
- Sei situar a pilha e o heap e digo em qual deles fica o ponteiro e em qual fica o bloco alocado.
- Uso
sizeofpara calcular o tamanho de um bloco (n * sizeof(tipo)) e não "chuto" o número de bytes. - Aloco com
malloc, guardo o retorno num ponteiro do tipo certo e testoNULLantes de usar. - Uso a memória alocada como um vetor comum (
v[i]). - Libero com
freeao terminar e façov = NULLem seguida. - Sei o que é um vazamento de memória e por que todo
mallocprecisa do seufree. - Sei a diferença entre
malloc(lixo) ecalloc(zerado) e quando cada um convém.
🔗 Referências e para se aprofundar¶
- Material 2026.1 — Aula 11: Ponteiros e Alocação Dinâmica.
- BACKES, André. Linguagem C: completa e descomplicada (ponteiros e alocação dinâmica de memória).
- KERNIGHAN, B.; RITCHIE, D. The C Programming Language (o clássico "K&R"), cap. 5 —
ponteiros e gerenciamento de memória (
malloc/free). - Documentação de referência: cppreference —
malloc(e, ao lado,calloc,reallocefree). - Prática: Beecrowd.
Antes de seguir para a Semana 13
Você deu o primeiro passo no gerenciamento manual de memória — o que separa quem "usa" C
de quem entende C. Certifique-se de que o "esqueleto" dos cinco passos (alocar →
verificar → usar → processar → liberar) está automático nas suas mãos. Na
Semana 13 levamos a alocação dinâmica adiante:
realloc para redimensionar um bloco já alocado, matrizes dinâmicas (ponteiro
para ponteiro) e um mergulho nos vazamentos de memória e nos erros clássicos
(use-after-free, double free). Tudo isso se apoia no que você firmou aqui — então não
avance sem ter digitado, executado e liberado os exemplos desta semana.