Estruturas de dados em C/C++

Array dinâmico e std::vector em C e C++

Nesta lição, você vai implementar em C um array dinâmico que cresce sozinho ao receber elementos e compará-lo com o std::vector de C++.

Introdução

Um array dinâmico (dynamic array) é um array que pode crescer quando necessário. Ele separa dois conceitos: a quantidade de elementos guardados (size) e o espaço alocado (capacity). Quando o array enche, o programa aloca uma área maior, normalmente o dobro, e passa os dados para lá.

Pontos importantes

  • Defina as condições que sempre devem ser verdadeiras (invariantes) antes de escrever as operações.
  • Trate os casos de estrutura vazia, cheia e de falha na alocação de memória.
  • Avalie ao mesmo tempo a complexidade e o gerenciamento de memória de cada operação.

Veja como funciona

Clique em push_back várias vezes. Quando o array enche, ele aloca um bloco com o dobro do tamanho e copia tudo.

Exemplos de código C e C++

C
Executar →
main.c
#include <stdio.h>
#include <stdlib.h>

int main(void) {
    size_t size = 0, capacity = 2;
    int *values = malloc(capacity * sizeof *values);
    if (!values) return 1;
    for (int value = 1; value <= 5; ++value) {
        if (size == capacity) {
            capacity *= 2;
            int *grown = realloc(values, capacity * sizeof *values);
            if (!grown) {
                free(values);
                return 1;
            }
            values = grown;
        }
        values[size++] = value * value;
    }
    printf("size=%zu capacity=%zu last=%d\n",
           size, capacity, values[size - 1]);
    free(values);
}
Resultado da execução
size=5 capacity=8 last=25
C++
Executar →
main.cpp
#include <iostream>
#include <vector>

int main() {
    std::vector<int> values;
    for (int value = 1; value <= 5; ++value) {
        values.push_back(value * value);
    }
    std::cout << "size=" << values.size()
              << " last=" << values.back() << '\n';
}
Resultado da execução
size=5 last=25

Comparação entre C e C++

Na versão em C, quando size chega a capacity, o programa dobra capacity e chama realloc para ampliar a memória. O resultado de realloc vai para um ponteiro temporário, grown, para que a memória antiga ainda possa ser liberada se a alocação falhar. O std::vector de C++ encapsula essa mesma estratégia de crescimento e ainda libera a memória sozinho, oferece iteradores e é seguro diante de exceções.

C

Os dados (struct) e as operações ficam separados, e a memória é alocada e liberada à mão.

C++

Classes e contêineres protegem a consistência dos dados e gerenciam os próprios recursos.

Exercícios práticos

Execute as duas versões e depois mude o código para observar a diferença de segurança que cada linguagem oferece.

  • Adicione testes para a estrutura vazia e para quando ela atinge a capacidade.
  • Escreva a liberação de memória e verifique se há vazamentos.
  • Compare a complexidade com o contêiner equivalente da biblioteca padrão.

Pratique com um desafio

Use o conteúdo desta lição para resolver um problema real. Cada desafio roda no navegador, com testes instantâneos e uma revisão por IA.

Todos os desafios