Punteros y memoria

Usar bien const en C y C++

En esta lección vas a usar la palabra clave const para declarar datos de solo lectura y a distinguir las distintas formas de combinar const con punteros.

Introducción

La palabra clave const permite que una función se comprometa a no modificar los datos que recibe. Si alguien modifica por error un dato marcado como const, el compilador avisa del error de inmediato. Así el código es más seguro, y quien lo lee ve fácilmente qué funciones pueden modificar los datos.

Puntos importantes

  • Un puntero solo debe apuntar a un objeto que todavía existe, del tipo correcto, o valer null.
  • Cada zona de memoria reservada debe liberarse exactamente una vez.
  • Deja claro qué función es dueña de la memoria (ownership) y qué datos solo se leen (const).

Ejemplos de código C y C++

C
Ejecutar →
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 de la ejecución
12
C++
Ejecutar →
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 de la ejecución
12

Comparación entre C y C++

El parámetro const int *values indica que la función sum lee los elementos del arreglo sin modificarlos. Lee la declaración empezando por el nombre de la variable: un puntero a datos const (const int *) protege el valor apuntado, mientras que un puntero const (int * const) no puede apuntar a otra dirección. C++ también tiene funciones miembro const, que se comprometen a no modificar el estado del objeto.

C

La propiedad y la liberación de la memoria dependen por completo del programador.

C++

Los contenedores y RAII liberan automáticamente los recursos cuando el objeto llega al final de su vida.

Ejercicios de práctica

Ejecuta las dos versiones y después modifica el código para ver la diferencia de seguridad que aporta cada lenguaje.

  • Prueba con punteros nulos y con fallos al reservar memoria.
  • Ejecuta el programa con AddressSanitizer y UndefinedBehaviorSanitizer para detectar errores de memoria.
  • Deja claro qué puntero es dueño de la memoria y cuál solo sirve para leer.