📚 Capítulo do curso: 15 — Alocação Dinâmica no Heap, Valgrind e Prevenção de Leaks da Especialização em Sistemas com C. Leia o capítulo antes de começar.

📈 Miniprojeto C 15 — Vetor Dinâmico Auto-expansível no Heap

Gerenciador de memória com malloc, realloc seguro e zero memory leaks.

⏱️ Tempo estimado 📦 Você vai entregar ✅ Pronto quando
55 min main.c a capacidade cresce 2 → 4 → 8 → 16, os 10 valores são impressos e tudo é liberado

Diagrama do 📈 Miniprojeto C 15 — Vetor Dinâmico Auto-expansível no Heap

🎯 Objetivos

📋 O problema

Arrays de tamanho fixo em C têm um problema: você precisa saber o tamanho antes de compilar. Um vetor dinâmico resolve isso guardando, além do array em si, dois números: quantos elementos ele pode guardar agora (capacidade) e quantos ele de fato guarda (tamanho). Quando tamanho alcança capacidade, o vetor pede mais memória ao sistema operacional via realloc, tipicamente dobrando a capacidade — estratégia que mantém o custo médio de inserção baixo mesmo crescendo o array centenas de vezes.

Seu programa (main.c) deve:

  1. Começar com um vetor de inteiros alocado no heap com capacidade inicial 2.
  2. Inserir 10 elementos, um a um (ex: múltiplos de 10: 10, 20, 30, …).
  3. Sempre que a capacidade encher, dobrar a capacidade via realloc antes de inserir o próximo elemento, imprimindo a nova capacidade a cada crescimento.
  4. Validar o retorno de todo malloc/realloc contra NULL, encerrando com mensagem de erro (sem crashar com segfault) se a alocação falhar.
  5. Ao final, imprimir todos os 10 elementos e liberar toda a memória com free.
ID Requisito
RF01 O programa deve compilar sem nenhum aviso com gcc -Wall -Wextra -Wpedantic -std=c17.
RF02 O vetor deve iniciar com malloc para capacidade 2, validando o retorno contra NULL.
RF03 O programa deve inserir exatamente 10 elementos inteiros, um por vez, em um laço.
RF04 Sempre que tamanho == capacidade, deve dobrar a capacidade com realloc antes de inserir o próximo elemento, e imprimir a nova capacidade.
RF05 O retorno de realloc deve ser guardado em uma variável temporária antes de sobrescrever o ponteiro original, para não perder a referência em caso de falha (NULL).
RF06 Ao final, deve imprimir todos os 10 elementos inseridos, na ordem de inserção.
RF07 Deve liberar toda a memória alocada com free antes de retornar, deixando o programa livre de vazamentos.
graph LR
    A["malloc(capacidade=2)"] --> B{"tamanho == capacidade?"}
    B -- "sim" --> C["realloc(capacidade * 2)\nvalida contra NULL"]
    C --> D["insere elemento"]
    B -- "nao" --> D
    D --> E{"todos os 10\nja inseridos?"}
    E -- "nao" --> B
    E -- "sim" --> F["imprime vetor final"]
    F --> G["free(vetor)"]

    style A fill:#0284c7,stroke:#38bdf8,color:#ffffff
    style C fill:#7c3aed,stroke:#a78bfa,color:#ffffff
    style G fill:#059669,stroke:#34d399,color:#ffffff

🧰 Ambiente

O mesmo do Miniprojeto 01: gcc 8 ou mais novo. No Windows, rode chcp 65001 se os emojis aparecerem quebrados.


Passo 1 — Compilar o esqueleto

🎯 Objetivo: partir de um código que compila e roda, antes de escrever qualquer linha.

⌨️ Faça: crie a pasta c_mini_15, salve o código abaixo como main.c e rode:

gcc -Wall -Wextra -Wpedantic -std=c17 main.c -o app
./app          # Linux/macOS
.\app.exe      # Windows (PowerShell)

📄 src/esqueleto.c

/*
 * Miniprojeto 15: Vetor Dinamico Auto-expansivel no Heap (Dynamic Array)
 * Especializacao em Engenharia de Sistemas em C
 */
#include <stdio.h>
#include <stdlib.h>

#define CAPACIDADE_INICIAL 2
#define TOTAL_ELEMENTOS 10

int main(void) {
    printf("====================================================\n");
    printf(" ⚡ Miniprojeto 15: Vetor Dinamico Auto-expansivel\n");
    printf("====================================================\n");

    size_t capacidade = CAPACIDADE_INICIAL;
    size_t tamanho = 0;

    // TODO 1: declare "int *vetor" recebendo malloc(capacidade * sizeof(int)) e
    // valide contra NULL (imprima erro em stderr e retorne EXIT_FAILURE).

    printf("\nCapacidade inicial: %zu\n\n", capacidade);

    for (int i = 0; i < TOTAL_ELEMENTOS; i++) {
        // TODO 2: se tamanho == capacidade, dobre a capacidade:
        //   - calcule nova_capacidade = capacidade * 2
        //   - chame realloc(vetor, nova_capacidade * sizeof(int)) e
        //     guarde o resultado em um ponteiro TEMPORARIO (ex: temp)
        //   - se temp == NULL, imprima erro, de' free(vetor) e retorne
        //     EXIT_FAILURE (NAO perca o ponteiro original antes de checar)
        //   - se deu certo: vetor = temp; capacidade = nova_capacidade;
        //   - imprima a nova capacidade

        int valor = (i + 1) * 10;
        // TODO 3: insira "valor" na posicao "tamanho" do vetor e
        // incremente "tamanho" (e apague o printf abaixo).
        printf("Ainda nao inserido: %d\n", valor);
    }

    printf("\n--- Conteudo final do vetor (%zu elementos) ---\n", tamanho);
    // TODO 4: percorra "vetor" de 0 ate "tamanho" e imprima cada elemento.

    // TODO 5: libere "vetor" com free() e atribua NULL a ele.

    printf("\n====================================================\n");
    printf(" ✅ Memoria liberada. Status: Operacional e Validado!\n");
    printf("====================================================\n");

    return EXIT_SUCCESS;
}

✅ Checkpoint: zero avisos do gcc, e a saída é:

🖥️ Saída esperada:

====================================================
 ⚡ Miniprojeto 15: Vetor Dinamico Auto-expansivel
====================================================

Capacidade inicial: 2

Ainda nao inserido: 10
Ainda nao inserido: 20
Ainda nao inserido: 30
Ainda nao inserido: 40
Ainda nao inserido: 50
Ainda nao inserido: 60
Ainda nao inserido: 70
Ainda nao inserido: 80
Ainda nao inserido: 90
Ainda nao inserido: 100

--- Conteudo final do vetor (0 elementos) ---

====================================================
 ✅ Memoria liberada. Status: Operacional e Validado!
====================================================

Os 10 valores são gerados, mas ainda não há onde guardá-los.


Passo 2 — Alocar no heap (TODO 1)

🎯 Objetivo: pedir memória ao sistema e nunca confiar cegamente na resposta.

⌨️ Faça: int *vetor = malloc(capacidade * sizeof(int)); e, se vetor == NULL, imprima um erro em stderr e return EXIT_FAILURE;.

✅ Checkpoint: compila sem avisos e a saída não muda. A alocação existe, mas ainda não é usada.

🧯 Se der errado: implicit declaration of function 'malloc' → falta #include <stdlib.h>.


Passo 3 — Inserir e crescer com realloc seguro (TODO 2 e 3)

🎯 Objetivo: dobrar a capacidade sem perder o bloco antigo se o realloc falhar.

⌨️ Faça:

  1. Antes de inserir, if (tamanho == capacidade): calcule nova_capacidade = capacidade * 2, faça int *temp = realloc(vetor, nova_capacidade * sizeof(int));. Se temp == NULL, faça free(vetor) e saia com erro. Senão, vetor = temp;, atualize a capacidade e imprima >> Capacidade cheia! Vetor cresceu para: %zu.
  2. Troque o printf("Ainda nao inserido...") por vetor[tamanho] = valor; tamanho++; e uma linha Inserido vetor[...].

✅ Checkpoint: aparecem exatamente três crescimentos: para 4 (antes do 30), para 8 (antes do 50) e para 16 (antes do 90).

🧯 Se der errado:


Passo 4 — Imprimir e liberar (TODO 4 e 5)

🎯 Objetivo: fechar o ciclo de vida da memória.

⌨️ Faça: percorra de 0 a tamanho imprimindo cada valor. Depois, free(vetor); vetor = NULL;.

✅ Checkpoint: --- Conteudo final do vetor (10 elementos) --- seguido de 10 20 30 40 50 60 70 80 90 100.

🧯 Se der errado: no Linux ou WSL, valgrind --leak-check=full ./app deve terminar com All heap blocks were freed -- no leaks are possible. Se aparecer definitely lost, faltou o free.


Passo final — Conferir com o gabarito

🎯 Objetivo: validar a saída completa e o código de retorno.

✅ Checkpoint: a saída é esta, e o código de retorno é 0 (echo $? no Linux/macOS ou echo $LASTEXITCODE no PowerShell):

🖥️ Saída esperada:

====================================================
 ⚡ Miniprojeto 15: Vetor Dinamico Auto-expansivel
====================================================

Capacidade inicial: 2

Inserido vetor[0] = 10 (tamanho=1, capacidade=2)
Inserido vetor[1] = 20 (tamanho=2, capacidade=2)
>> Capacidade cheia! Vetor cresceu para: 4
Inserido vetor[2] = 30 (tamanho=3, capacidade=4)
Inserido vetor[3] = 40 (tamanho=4, capacidade=4)
>> Capacidade cheia! Vetor cresceu para: 8
Inserido vetor[4] = 50 (tamanho=5, capacidade=8)
Inserido vetor[5] = 60 (tamanho=6, capacidade=8)
Inserido vetor[6] = 70 (tamanho=7, capacidade=8)
Inserido vetor[7] = 80 (tamanho=8, capacidade=8)
>> Capacidade cheia! Vetor cresceu para: 16
Inserido vetor[8] = 90 (tamanho=9, capacidade=16)
Inserido vetor[9] = 100 (tamanho=10, capacidade=16)

--- Conteudo final do vetor (10 elementos) ---
10 20 30 40 50 60 70 80 90 100 

====================================================
 ✅ Memoria liberada. Status: Operacional e Validado!
====================================================

Só abra o gabarito depois de chegar até aqui:

🔑 Ver gabarito: src/main.c

📄 src/main.c

/*
 * Miniprojeto 15: Vetor Dinamico Auto-expansivel no Heap (Dynamic Array)
 * Especializacao em Engenharia de Sistemas em C
 */
#include <stdio.h>
#include <stdlib.h>

#define CAPACIDADE_INICIAL 2
#define TOTAL_ELEMENTOS 10

int main(void) {
    printf("====================================================\n");
    printf(" ⚡ Miniprojeto 15: Vetor Dinamico Auto-expansivel\n");
    printf("====================================================\n");

    size_t capacidade = CAPACIDADE_INICIAL;
    size_t tamanho = 0;

    int *vetor = malloc(capacidade * sizeof(int));
    if (vetor == NULL) {
        fprintf(stderr, "ERRO: falha ao alocar memoria inicial.\n");
        return EXIT_FAILURE;
    }

    printf("\nCapacidade inicial: %zu\n\n", capacidade);

    for (int i = 0; i < TOTAL_ELEMENTOS; i++) {
        if (tamanho == capacidade) {
            size_t nova_capacidade = capacidade * 2;
            /* Guardamos o resultado do realloc em "temp" primeiro: se a
             * alocacao falhar, "vetor" continua valido e podemos liberar
             * a memoria original sem vaza-la (nunca faca
             * "vetor = realloc(vetor, ...)" diretamente). */
            int *temp = realloc(vetor, nova_capacidade * sizeof(int));
            if (temp == NULL) {
                fprintf(stderr, "ERRO: falha ao realocar memoria.\n");
                free(vetor);
                return EXIT_FAILURE;
            }
            vetor = temp;
            capacidade = nova_capacidade;
            printf(">> Capacidade cheia! Vetor cresceu para: %zu\n", capacidade);
        }

        int valor = (i + 1) * 10;
        vetor[tamanho] = valor;
        tamanho++;
        printf("Inserido vetor[%zu] = %d (tamanho=%zu, capacidade=%zu)\n",
               tamanho - 1, valor, tamanho, capacidade);
    }

    printf("\n--- Conteudo final do vetor (%zu elementos) ---\n", tamanho);
    for (size_t i = 0; i < tamanho; i++) {
        printf("%d ", vetor[i]);
    }
    printf("\n");

    free(vetor);
    vetor = NULL;

    printf("\n====================================================\n");
    printf(" ✅ Memoria liberada. Status: Operacional e Validado!\n");
    printf("====================================================\n");

    return EXIT_SUCCESS;
}

Por que isso importa: repare a sequência de capacidades impressas: 2 → 4 → 8 → 16. Mesmo inserindo 10 elementos, o vetor só realocou 3 vezes, não 8 — essa é a mágica do crescimento geométrico, que é exatamente o que std::vector::push_back faz por baixo dos panos em C++.


🏆 Desafio

Adicione uma função void inserir(int **vetor, size_t *tamanho, size_t *capacidade, int valor) que encapsule toda a lógica de crescimento (recebendo os três valores por ponteiro, já que main precisa enxergar as mudanças). Depois, insira 1000 elementos em vez de 10 e conte, com um contador extra, quantas vezes o realloc foi de fato chamado — compare com log2(1000) para confirmar que o crescimento continua sendo logarítmico no número de realocações.

✅ Checkpoint: com 1000 elementos a partir da capacidade 2, o realloc é chamado 9 vezes (2 → 4 → … → 1024), perto de log2(1000) ≈ 10. A capacidade final é 1024.


🧭 Navegação

📋 Projetos do curso de C | 📚 Teoria do Capítulo 15 | 🗂️ Hub de projetos