Los punteros en un contexto de programación son variables que tienen un comportamiento especial, de tal manera que su objetivo es guardar posiciones de memoria para acceder al valor de estos, además, de esta manera se puede optimizar el consumo de recursos en un programa, evitando redundancia de declaración de datos, sin embargo, un mal manejo de esto podría implicar mayor consumo de memoria de forma innecesaria.
[ESTRUCTURA DE DATOS] ESTRUCTURA DE DATOS: PUNTEROS
Si bien los punteros es una buena forma de optimizar un programa haciendo uso adecuado de los recursos, no siempre se realiza un buen uso de estos, ya que en muchas ocasiones no se libera correctamente la memoria, lo cual debe hacerlo de forma explícita, el programador; entonces, hay que considerar cuidadosamente estos casos.
Mencionado lo anterior, veamos algunos ejemplos de uso de punteros, paso a paso:
Ejemplo básico de puntero de tipo entero en C++
Si bien un puntero puede ser de diferentes tipos de datos, trabajemos con un tipo entero; para lo cual, veamos la manera correcta de inicializarlo.
int *pointer = nullptr;
Veamos cuidadosamente la línea de código anterior: pointer es la variable declarada (puede ser por ejemplo: xyz, var, numero o cualquier otro nombre que se le decida poner), y, el asterísco (*) sería la instrucción que indica que dicha variable se comportará como un puntero, finalmente, nullptr es la asignación inicial del puntero (en este caso, una posición de memoria nula).
Explicado lo anterior, debemos recordar que un puntero debe apuntar a una posición de memoria, por lo tanto, nullptr es una inicialización a una posición de memoria inexistente o nula, entonces, la pregunta sería ¿Cómo asignamos una posición de memoria específica a un puntero? para responder esto, trabajemos con una segunda variable de tipo entero pero que no sea puntero.
int var = 7;
Declarado la variable anterior, ahora asignemos la posición de memoria de esta variable, al puntero previamente declarado.
pointer = &var;
Observemos cuidadosamente lo anterior; a pointer, se le asigna &var, donde & significa el acceso a la posición de memoria de var, de esta manera, pointer apunta a la posición de memoria mas no al valor de la variable var; entonces, ¿esto que quiere decir? pues veamos imprimiendo los valores de diversas maneras.
std::cout << &var << std::endl;
std::cout << var << std::endl;
std::cout << pointer << std::endl;
std::cout << *pointer << std::endl;
Para el primer caso, la salida es: 0x71ff08
Para el segundo caso, la salida es: 7
Para el tercer caso, la salida es: 0x71ff08
Para el cuarto caso, la salida es: 7
Pasemos a explicar cada caso: para &var se imprime la posición de memoria que ocupa la variable (0x71ff08), mientras que para var hace lo de siempre, es decir, imprime el valor de la variable (7); seguidamente, para pointer se imprime la posición de memoria de este puntero donde previamente se le asignó la posición de memoria de var al hacer pointer = &var; (0x71ff08 "No requiere agregar &, porque al ser un puntero, este ya almacena la posición de memoria en sí") y para acceder al valor que almacena la posición de memoria del puntero, se añade un asterísico al inicio de pointer (*pointer), de tal manera que esto imprime el valor (7).
Pues así, tenemos diferentes maneras de acceder a las variables, posición de memoria de variables, posiciones de memoria y valores que aloja un puntero.
Cambio de valor de un puntero y variable enlazada por memoria (enlace por referencia)
En base al ejemplo anterior planteado, continuemos agregando más código, por ejemplo, lo siguiente.
*pointer = 10;
std::cout << var << std::endl;
std::cout << *pointer << std::endl;
¿Qué sucede aquí? pues si vemos la salida de estas instrucciones añadidas, veremos que en pantalla se muestra 2 veces el valor (10) ¿y esto por qué ocurre?, pues como pointer apunta a la posición de memoria de var, y mediante la instrucción asterisco estamos asignando un nuevo valor a la posición de memoria de pointer, pues se asigna el valor 10, tanto al puntero, como a var (en manera aparentemente automática); esto sucede porque al apuntar pointer a la posición de memoria de var, cualquier cambio que se haga a uno u otro, afectará a las 2 variables (variable simple y puntero). Dicho lo anterior, si hacemos lo siguiente, se imprimirá en pantalla 2 veces el valor (20), esto por lo ya explicado que un cambio en una variable, afectará tanto a la variable simple como al puntero.
var = 20;
std::cout << var << std::endl;
std::cout << *pointer << std::endl;
Declaración de variable con memoria dinámica (uso de memoria dinámica a través de un puntero)
Visto lo anterior, puede que nos preguntemos ¿si trabajo con punteros siempre tengo que enlazarlo a una variable previamente declarada? y la respuesta es: pues NO; en realidad también podemos declarar directamente una posición en memoria para el trabajo con un puntero, y, esto lo hacemos de la siguiente manera.
int *x = new int;
*x = 7;
std::cout << x << std::endl;
std::cout << *x << std::endl;
En el código anterior, new int reserva un espacio de memoria, el cual es asignado a x (claro que necesariamente declarado como un puntero) y luego puede usarse directamente ese puntero, asignándole algún valor e imprimiendo este en pantalla o haciendo lo que pudiera ser necesario con este (el primer cout muestra la posición en memoria reservada y el segund cout muestra el valor del puntero "como ya lo vimos en casos anteriores").
Explicado lo anterior y ya el programa en funcionamiento, debemos saber que es muy importante borrar la memoria reservada luego de usarlo, es decir, cuando ya no necesitemos hacer nada con el puntero que declaramos; para esto, debemos escribir la siguiente instrucción.
delete x;
Lo anterior libera la memoria y de esta manera no tendremos espacio ocupado innecesariamente (recordar que a esta instrucción debe llamarse estrictamente cuando ya no se usará el puntero).
CONCLUSIÓN
El uso de punteros en C++ representa una herramienta poderosa para la manipulación directa de memoria, permitiendo optimizar recursos y lograr un control detallado sobre los datos. Sin embargo, también implica una gran responsabilidad, ya que una mala gestión puede provocar errores graves como fugas de memoria. Por ello, es fundamental comprender su funcionamiento y liberar adecuadamente la memoria cuando ya no se necesita, garantizando así un manejo seguro y eficiente.
No hay comentarios:
Publicar un comentario