28/03/2024
# Tags
#C++ #Programación

Punteros en C++

Todos los lenguajes de programación tienen una característica particular que los distingue del resto, algunos tienen más marcada su «personalidad» que otros. Es el caso de C++ una característica fundamental del lenguaje son los punteros. Se puede señalar que C++ tiene el alcance y potencia que tiene por la implementación de los punteros. Finalmente no debemos olvidar que son herencia del lenguaje C.

Además hay que señalar que el tema de los punteros siempre es causa de conflicto y, porque no decirlo, desesperación de programadores, más si son nuevos o solo han utilizado lenguajes de alto nivel. Solo puedo decir que sí, cuesta trabajo asimilar con claridad el uso de los punteros, pero las horas o días que dediques a esto, valdrán la pena.

La tercera idea que hay que mencionar es que los punteros son los que en gran medida le dan la potencia que tiene C++, son los que hacen que C++ llegue a donde los otros lenguajes de alto nivel no pueden.

¿Para qué se utilizan los punteros?

En C++ utilizamos los punteros para acceder a la memoria y manipular las direcciones de memoria.

Direcciones de memoria en C++

Para entender a los punteros primero debes conocer como se almacenan los datos en la memoria de la computadora.

Cada que defines una variable en tu programa es asignada en una ubicación física en la memoria de la computadora. El valor que almacena dicha variable es almacenado en la ubicación asignada.

Para conocer en donde están almacenados los datos, C++ cuenta con el operador & (ampersand). En punteros el operador & se denomina operador de referencia. El operador & te indica la dirección ocupada por una variable.

En algunos textos el operador & recibe el nombre de operador de dirección

Sintaxis para obtener una dirección de memoria

&Variable

en donde,

&, es el operador de referencia, se de poner siempre antes del nombre de la variable
Variable, variable de la cual deseamos obtener su dirección en memoria.

Si edad es una variable, &edad devolverá la dirección de esta variable.

// cpp_52_punteros1.cpp
// Este programa forma parte del tutorial de introducción al lenguaje C++
// http://a.ehack.info/leguaje-c-introduccion/
// Se ilustra el acceso a la dirección que ocupan las varibles en memoria.
// 2018, Por http://about.me/carlosgbr
// Versión 1
// Compilado en https://www.onlinegdb.com/online_c++_compiler
#include <iostream>
using namespace std;
int main()
{
    int var1 = 3;
    int var2 = -98;
    int var3 = 25;
    cout << &var1 << endl;
    cout << &var2 << endl;
    cout << &var3 << endl;
    return 0;
}

El 0x indica que las direcciones mostradas están en formato hexadecimal.

Observa que entre cada variable hay una diferencia de 4 bytes, esto se debe a que en sistemas de 64 bits una variable entera ocupa 4 bytes.

Punteros a variables

C++ te da la posibilidad de manipular el contenido de las variables en memoria de forma directa. Puedes asignar o desasignar cualquier espacio de memoria como desees. Esto se realiza utilizando punteros a variables.

Los punteros a variables son variables que apuntan a una dirección específica en la memoria apuntada por otra variable.

Sintaxis para declarar un puntero a una variable

tipoDeDato *NombreVariable;
o,
tipoDeDato* NombreVariable;

en donde,

tipoDeDato, tipo de dato válido en C++
*, obligatorio, el asterisco es el operador de des referencia y puede ser leído como «el valor apuntado por«
NombreVariable, Nombre de la variable.

Por ejemplo,

int *p;
o
int* p;

Define un puntero a la variable p, y tiene la dirección de la memoria de p

Operador de referencia (&) y Operador de des referencia (*)

Como se mencionó anteriormente, el operador de referencia nos da la dirección de una variable.

Para obtener el valor almacenado en una dirección de memoria, utilizamos el operador de des referencia (*)

Por ejemplo si la variable numero se encuentra almacenada en la dirección &123, y ésta contiene el valor 5.

El operador de referencia (&) te dará el valor &123, mientras el operador de des referencia (*) devuelve el valor 5

Nota. El signo (*) utilizado en la declaración del puntero C ++ no es el puntero de des referencia. Es solo una notación similar que crea un puntero.

Declaración de varios punteros

En la siguiente sentencia,

int p1, p2, p3;

las variables p1, p2, p3; son de tipo int. Tal como aprendimos en el apartado de declaración de variables.

Ahora considera,

int* p1, p2, p3;

De acuerdo a lo que sabemos podemos suponer que p1, p2, p3 son 3 variables puntero. Pero no es así, en la sentencia anterior tenemos que p1 es un puntero a un int, pero p2 y p3 son de tipo int.

Si queremos que las 3 variables sean puntero, es necesario declararlo de forma explícita:

int* p1, *p2, *p3;

o bien,

int* p1; //equivalente a int *p3;
int* p2; //equivalente a int *p3;
int* p3; //equivalente a int *p3;

puedes comprobar esto fácilmente con un pequeño programa,

// cpp_53_punteros2.cpp
// Este programa forma parte del tutorial de introducción al lenguaje C++
// http://a.ehack.info/leguaje-c-introduccion/
// Se ilustra la declaración de punteros en la misma línea
// 2018, Por http://about.me/carlosgbr
// Versión 1
// Compilado en https://www.onlinegdb.com/online_c++_compiler
#include <iostream>
using namespace std;
int main()
{
// Primero compila (ejecuta) el programa como se presenta,
// Una vez que compruebes que el programa compila, quita los comentarios de la
// siguiente sección y vuelve a compiar. Compara el resultado.
/*    int var = 50;
    int* p1, p2, p3;
    p1 = &var;      // guardamos el valor de la dirección en el puntero
    p2 = &var;      // devuelve el error error: invalid conversion from 'int*' to 'int' [-fpermissive]
    p3 = &var;      // devuelve el error error: invalid conversion from 'int*' to 'int' [-fpermissive]
 */
    int var = 50;
    int* h1, *h2, *h3;
    h1 = &var;      // guardamos el valor de la dirección en el puntero
    h2 = &var;      // devuelve el error error: invalid conversion from 'int*' to 'int' [-fpermissive]
    h3 = &var;      // devuelve el error error: invalid conversion from 'int*' to 'int' [-fpermissive]
    return 0;
}

De este modo comprobamos fácilmente que para declarar varios punteros en la misma línea, necesitamos utilizar el * para cada variable.

Ejemplo. Punteros en C++

// cpp_54_punteros3.cpp
// Este programa forma parte del tutorial de introducción al lenguaje C++
// http://a.ehack.info/leguaje-c-introduccion/
// Se ilustra el uso y definición de punteros en C++.
// Este ejemplo se adapta de https://www.programiz.com/cpp-programming/pointers
// Versión 1
// Compilado en https://www.onlinegdb.com/online_c++_compiler
#include <iostream>
using namespace std;
int main()
{
    int *pc, c;
    c = 5;
    cout << "Dirección de c (&c): " << &c << endl;
    cout << "Valor de (c): " << c << endl << endl;
    pc = &c;  // El puntero pc almacena la dirección de memoria de la variable c
    cout << "La dirección del puntero pc almacena (pc): "<< pc << endl;
    cout << "El contenido de la dirección del puntero pc almacena (*pc): " << *pc << endl << endl;
    c = 11;    // El contenido dentro de la dirección de memoria &c se cambia de 5 a 11.
    cout << "La dirección del puntero pc almacena (pc): " << pc << endl;
    cout << "El contenido de la dirección del puntero pc almacena (*pc): " << *pc << endl << endl;
    *pc = 2;
    cout << "Dirección de c (&c): " << &c << endl;
    cout << "Valor de c (c): " << c << endl << endl;
    return 0;
}

Salida del programa,

Dirección de c (&c): 0x7ffea530a34c
Valor de (c): 5
La dirección del puntero pc almacena (pc): 0x7ffea530a34c
El contenido de la dirección del puntero pc almacena (*pc): 5
La dirección del puntero pc almacena (pc): 0x7ffea530a34c
El contenido de la dirección del puntero pc almacena (*pc): 11
Dirección de c (&c): 0x7ffea530a34c
Valor de c (c): 2

Primero te muestro de forma gráfica lo que ocurre:

Punteros: Cómo funcionan
Punteros: Cómo funcionan

Explicación del programa

  • Cuando c = 5, el valor 5 se almacena en la dirección de la variable c – 0x7ffea530a34c
  • Cuando pc = &c; el puntero pc almacena la dirección de c – 0x7ffea530a34c, y la expresión (operador de des referencia) *pc muestra el valor almacenado en esta dirección, es decir 5.
  • Cuando c = 11; debido a que la dirección del puntero pc almacenada es la misma que c – 0x7ffea530a34c, el cambio en el valor de c se ve reflejado así mismo cuando la expresión *pc es ejecutada. que ahora muestra 11.
  • Cuando *pc = 2; esto cambio el contenido almacenado en la dirección de pc – 0x7ffea530a34c. Esto es cambiado de 11 a 2. De este modo, cuando imprimimos el valor de c, el valor será 2 también.

Inicialización de punteros.

Los punteros requieren ser inicializados al igual que cualquier otra variable, de hecho es más relevante en el tema de los punteros.

La inicialización de un puntero se hace asignando 0 a un puntero.

int* h1 = 0;
char *h2 = 0;
bool *h3 = 0;

en algunos textos se propone el uso de NULL, para inicializar variables, recordemos que NULL en muchas librerías se define como 0L (0 entero largo), sin embargo se sugiera la asignación de 0 de forma directa.

Errores comunes al utilizar punteros

Supongamos que quieres que el puntero pc apunte a la dirección de c, entonces.

int c, *pc;
pc = c; // ¡Mal! pc tiene una dirección mientras c no es una dirección
*pc = &c; // ¡Mal! *pc es el valor apuntado por la dirección muestras &c es una dirección
pc = &c; // ¡Correcoto! pc es una dirección y &pc es también una dirección
*pc = c; // ¡Correcto! *pc es el valor apuntado por la dirección y, c es así mismo su valor

En ambos casos, el puntero pc no está apuntando a la dirección de c

¿Confundido con los punteros?

Bueno, no es para menos, si nunca los has usado o estudiado lo anormal sería que no tuvieras dudas. A continuación te presento una analogía y otro ejemplo con la intención de dejar más claro el concepto.

Si tu formación es de sistemas, ingenieril o simplemente tienes curiosidad, te sugiero ampliamente que leas el excelente articulo de C con clase, Punteros, complementa muy bien esta lección y te presenta algunos temas avanzados. En el tema de punteros mejor que nunca es que mientras más información tengas, mejor.

Llamadas por referencia.

  • Supongamos que tenemos una taza llamada A y tiene café y la taza B tiene té.
  • La tarea que deseas realizar es pasar el té en la taza A y el café en la B. ¿Cómo lo haces?  Nota: No tienes una taza vacía para hacer un vertido intermedio.
  • La respuesta es simple, puedes cambiar la etiqueta A a B y B a A. De este modo ahora tenemos té en la taza A y en la taza B café.

Aquí la taza es la variable las etiquetas A y B son direcciones de memoria, el café y el té son los datos almacenados en la variable, el puntero eres tú (tú eres quien realiza el cambio de etiquetas en las tazas)

Así es como funcionan los punteros.

Ejemplo

// cpp_55_punteros4.cpp
// Este programa forma parte del tutorial de introducción al lenguaje C++
// http://a.ehack.info/leguaje-c-introduccion/
// Se ilustra la manipulación de variables con punteros.
// 2018, Por http://about.me/carlosgbr
// Versión 1
// Compilado en https://www.onlinegdb.com/online_c++_compiler
#include <iostream>
using namespace std;
int main()
{
    int var = 50;   // definimos una variable "normal"
    int  *p;        // definimos *p como puntero a un entero
    p = &var;       // guardamos el valor de la dirección en el puntero
    cout << var << endl;
    // Muestra 50 (el valor de var)
    cout << p << endl;
    // muestra 0x7fff0e40086c (la dirección de memoria de var)
    cout << *p << endl;
    /* Muestra 50 (el valor de la variable
     almacenada en el puntero) */
    *p = 100;
    /* cambiaos el valor almacenado dentro de la
     variable con la ayuda del puntero */
    cout << *p << endl;
    /* Muestra 100 (el valor de la variable
     almacenada en el puntero p) */
    cout << var << endl;
   /* Muestra 100. (el nuevo valor de var que fue asignado se debió a *p=100) */
    return 0;
}

Como notas adicionales al programa anterior considera:

&var - obtiene la dirección de var
p = &var; - podemos hacer esta asignación debido a que p es de tipo puntero.
cout << p; - muestra la dirección almacenada en p,
cout << *p; - recupera el valor almacenado en la dirección (el operador de desreferencia * es quien realiza esta recuperación)
*p = 100; - asigna 100 a la dirección apuntada por p NOTA: no puedes asignar p = 100; debido a que p es puntero y debe almacenar forzosamente una dirección.

Conclusión

El uso de punteros ocasiona opiniones encontradas siempre, lo único que puedo decir a título personal es que no es tan críptico como parece, pero tampoco tan simple como algunos refieren. Los punteros requieren abstracción y paciencia y sobre todo, práctica.

Esta es una primer aproximación a los punteros, normalmente en los textos se aborda a continuación los punteros a Arrays, a funciones y objetos. En nuestro caso primero abordaremos un último tema «core» antes de realmente empezar a explorar la potencia de C++ y las librerías: las funciones.


Ethical Hack

Referencias

  • (1) La columna vertebral de este artículo se escribió a partir de la publicación de la columna Learn C++ de Programiz.

Fuente Imágenes

  • «Todas las demás imágenes de esta sección»: by Nala Systems

Código Fuente

Licencia de Creative Commons
Punteros en C++ by Roberto C. González is licensed under a Creative Commons Reconocimiento-NoComercial-CompartirIgual 4.0 Internacional License.
Punteros en C++

Arrays en C++ – y II

Punteros en C++

Unas palabras a medio camino

Leave a comment

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *