Ponteiros e memória

Usar const corretamente em C e C++

Nesta lição, você vai usar a palavra-chave const para declarar dados somente leitura e diferenciar os usos de const com ponteiros.

Introdução

A palavra-chave const permite que uma função prometa não alterar os dados que recebe. Se alguém modificar sem querer um dado marcado como const, o compilador acusa o erro na hora. Isso deixa o código mais seguro e facilita para quem lê saber quais funções podem alterar dados.

Pontos importantes

  • Um ponteiro só deve apontar para um objeto que ainda existe, do tipo certo, ou ser nulo.
  • Cada área de memória alocada deve ser liberada exatamente uma vez.
  • Deixe claro qual função é dona da memória (ownership) e quais dados são apenas lidos (const).

Exemplos de código C e C++

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

int sum(const int *values, size_t count) {
    int total = 0;
    for (size_t i = 0; i < count; ++i) total += values[i];
    return total;
}

int main(void) {
    const int values[] = {2, 4, 6};
    printf("%d\n", sum(values, 3));
    return 0;
}
Resultado da execução
12
C++
Executar →
main.cpp
#include <array>
#include <iostream>

int sum(const std::array<int, 3>& values) {
    int total = 0;
    for (int value : values) total += value;
    return total;
}

int main() {
    const std::array values{2, 4, 6};
    std::cout << sum(values) << '\n';
}
Resultado da execução
12

Comparação entre C e C++

O parâmetro const int *values indica que a função sum só lê os elementos do array, sem alterá-los. Leia a declaração a partir do nome da variável: um ponteiro para dado const (const int *) protege o valor apontado, enquanto um ponteiro const (int * const) não pode apontar para outro endereço. C++ também tem funções-membro const, que prometem não alterar o estado do objeto.

C

A posse e a liberação da memória ficam totalmente por conta do programador.

C++

Contêineres e RAII liberam os recursos automaticamente quando o objeto chega ao fim da vida.

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.

  • Teste ponteiros nulos e falhas na alocação de memória.
  • Execute o programa com AddressSanitizer e UndefinedBehaviorSanitizer para encontrar erros de memória.
  • Anote claramente qual ponteiro é dono da memória e qual só serve para leitura.