const int*, int * const, const int * const: Desglose sangriento de punteros

Entrenador de Python en Línea para Principiantes

Aprende Python fácilmente sin sobrecargarte de teoría. Resuelve tareas prácticas con verificación automática, recibe pistas y escribe código directamente en el navegador, sin instalar nada.

Start Course

Introducción: ¿Por qué este tema vuelve locos incluso a desarrolladores experimentados de C++?

\n\n

Si alguna vez has abierto código ajeno en C++ y has visto algo como const int * const data, seguramente has sentido el deseo de cerrar el editor e irte a tomar un té. Esta extraña sintaxis con const esparcido a ambos lados del asterisco es una verdadera piedra de tropiezo. La pregunta «What is the difference between const int*, const int * const, and int * const?» en Stack Overflow ha acumulado cientos de miles de visitas. Y no es casualidad.

\n\n

Entender estas construcciones no es solo un capricho de un pedante. Es la clave para escribir código seguro, autodocumentado y, lo que es más importante, para pasar entrevistas. Hoy vamos a aclarar esta confusión de una vez por todas, usando una regla simple que siempre funciona.

\n\n

Los tres pilares: ¿qué vamos a comparar?

\n\n

En C++, los punteros y la constancia pueden combinarse de cuatro maneras (tres de ellas son las más populares). Vamos a nombrarlas:

\n\n
    \n
  • Opción 1: const int* ptr (o int const* ptr — es lo mismo).
  • \n
  • Opción 2: int * const ptr.
  • \n
  • Opción 3: const int * const ptr (combinación de las dos primeras).
  • \n
\n\n

La idea clave: const siempre se refiere a lo que está a su izquierda. Si no hay nada a la izquierda, se refiere a lo que está a la derecha. Suena simple, pero en la práctica esta regla es salvadora.

\n\n

Opción 1: const int* ptr (Puntero a un entero constante)

\n\n

Leemos de derecha a izquierda: «ptr es un puntero a un número entero que es constante».

\n\n

¿Qué significa esto en la práctica?

\n
    \n
  • No puedes cambiar el valor de la variable a la que apunta ptr a través de este puntero.
  • \n
  • Sí puedes cambiar el puntero en sí mismo (hacer que apunte a otra variable).
  • \n
\n\n
#include <iostream>\n\nint main() {\n    int a = 10;\n    int b = 20;\n\n    const int* ptr = &a;  // ptr — puntero a const int\n\n    // *ptr = 15; // ¡ERROR de compilación! No se puede cambiar el valor a través de const int*\n    std::cout << "*ptr = " << *ptr << std::endl; // Mostrará 10\n\n    ptr = &b; // ¡OK! Cambiamos el puntero en sí\n    std::cout << "*ptr = " << *ptr << std::endl; // Mostrará 20\n\n    return 0;\n}
\n\n

Este tipo se usa a menudo para pasar datos a una función cuando quieres garantizar que la función no dañe los datos originales. Por ejemplo, void print(const int* data) es una promesa de «solo lectura».

\n\n

Opción 2: int * const ptr (Puntero constante a un número entero)

\n\n

Leemos de derecha a izquierda: «ptr es un puntero constante a un número entero».

\n\n

Aquí es al revés:

\n
    \n
  • Sí puedes cambiar el valor de la variable a la que apunta ptr.
  • \n
  • No puedes cambiar el puntero en sí mismo (siempre debe apuntar a la misma dirección de memoria).
  • \n
\n\n
#include <iostream>\n\nint main() {\n    int a = 10;\n    int b = 20;\n\n    int * const ptr = &a; // ptr — puntero constante a int\n\n    *ptr = 15; // ¡OK! Cambiamos el valor a través del puntero\n    std::cout << "*ptr = " << *ptr << std::endl; // Mostrará 15\n\n    // ptr = &b; // ¡ERROR de compilación! No se puede cambiar el puntero en sí\n\n    return 0;\n}
\n\n

Imagina que int * const es un puntero «fijo». Se usa a menudo para trabajar con registros de hardware o en casos donde el puntero a un búfer no debe «escaparse» a otro lugar.

\n\n

Opción 3: const int * const ptr (Puntero constante a un entero constante)

\n\n

Leemos de derecha a izquierda: «ptr es un puntero constante a un número entero, que es constante».

Blogs

Book Recommendations