Saludos estimados lectores. En el capítulo anterior se presentó el tema de las funciones en C++. Notemos que con ellas se cambia un poco el paradigma heredado de los lenguajes de bajo nivel adoptando, ahora, un enfoque más funcional, permitiendo segmentar ciertos bloques de código que cumplen un objetivo concreto y poderlo reutilizar en otros sectores del código principal sin la necesidad de copiar nuevamente el bloque de instrucciones. Esto inclusive fortalece el paradigma modular en la programación. Ahora nos enfrentamos a un nuevo tema: Punteros. Pero antes de comenzar con esto preguntémonos ¿Qué son los punteros?
Un puntero es un objeto que apunta a la dirección de memoria de una variable, arreglo, función o estructuras más complejas, lo cual se constituye como una herramienta indispensable para los sistemas embebidos, además de proporcionarnos soporte para asignación de memoria dinámica, agregando, a su vez, otra dimensión al control de flujo en un programa.
Los usos más comunes para los punteros son:
- Crean código eficiente y rápido.
- Proporcionan asignación de memoria dinámica.
- Hacen expresiones compactas y concisas.
- Protegen datos pasados como parámetros a una función.
- Proporcionan la capacidad de pasar estructuras de datos mediante un puntero sin ocasionar un exceso de código conocido como “overhead”.
En este curso, por ser básico, su interés es que sepas manejarlos con facilidad, dejando su aplicación de optimización para otros temas más avanzados. Por lo que se enfocará en cómo estos son utilizados para apuntar a una dirección de memoria de una variable. Lo que nos lleva a iniciar explicando el direccionamiento en las memorias.
Direcciones de Memorias
Al hablar de memoria nos referimos a la RAM, a la volátil, aquella que almacena información de manera momentánea para que nuestras aplicaciones puedan ejecutarse con rapidez y facilidad. Ahora para los efectos de programación veamos a la memoria como un gran bloque cuya capacidad esta definida por la unidad de medida el byte (8 bits). Ese bloque de memoria se encuentra segmentada en varios bloques más pequeños con capacidad de almacenamiento de un byte, en la cual cada una de esa división tiene asignada una dirección para ubicar mejor el dato almacenado.
Normalmente me gusta hacer comparación de esta visión con una edificio residencial. Supongamos que el edificio existen 100 apartamentos, con identificación y dentro de esos apartamentos viven cierta cantidad de personas. Pues digamos entonces que el edificio es la memoria, el cual tiene 100 bloques de almacenamiento (los apartamentos) identificados por una dirección (nro del apartamento) y dentro de cada bloque esta almacenado un dato (cantidad de personas que viven en el apartamento), así si requiero saber el dato (cantidad de personas) de la dirección 50 (nro de apartamento 50), obtendré la información solicitada (número de personas que residen en ese apto).
Gráficamente se puede representar así:
Fíjense que en la dirección 0001 la memoria tiene almacenado el dato BB y en la 0002 el dato AA. Ahora, ¿Es necesario conocerse las direcciones? Pues no, lo único que debemos saber es que podemos apuntar a una dirección que almacena un valor de una variable y de allí poder explorar la memoria a nuestro antojo.
Punteros
Lo anterior es una breve descripción de la memoria y su direccionamiento como conocimiento previo para poder entender un poco el tema de los punteros. Al inicio ya se dijo que era un puntero. Ahora veamos su aplicación práctica. Comencemos con su sintaxis de inicialización:
[tipo de dato] *nombre_del_puntero.
#include <stdlib.h> #include <iostream> using namespace std; int main () { int *p1; float *p2; char *p3; }
Se puede notar que la sintáxis es similar a la declaración de una variable con la salvedad que al nombre del puntero lo antecede el operador asterisco (*). A su vez se puede observar que se puede crear un puntero con cualquiera de los tipos de datos conocidos en C++.
Para concluir estudiemos el siguiente código:
#include <stdlib.h> #include <iostream> using namespace std; int main () { int num1, num2; int *p1, *p2; p1 = &num1; p2 = &num2; num1 = 10; num2 = 20; cout << "De esta forma muestro la dirección a las que apuntan p1 y p2" << endl; cout << "p1 = " << p1 << " p2 = " << p2 << endl; cout << "De esta forma muestro los datos que almacenan sus direcciones:" << endl; cout << *p1 << " - " << *p2; p1 = p2; p2 = 0; return 0 }
Bien, analicemos el ejercicio:
- Se crean dos variables num1 y num2 tipo entero (int) y dos punteros p1 y p2 también tipo entero ya que son creados con la finalidad de apuntar a las direcciones de las variables. Es importante que los punteros y variables que se vayan a relacionar tenga el mismo tipo de dato.
- Luego se asigna la dirección a la que apunta la variable num1 al puntero p1; lo mismo ocurre con la variable num2 y el puntero p2. Nótese que para indicar que un puntero apuntará a la dirección de una variable se debe anteceder al nombre de la variable con el operador AND (&), si esto se obvia el depurador dará un error. A pesar que las variable no tengan algún valor asignado, éstas ya tienen una dirección de ubicación en la memoria, es por eso que se le puede pasar esa información a los punteros.
- Luego se le asigna un valor a num1 y num2, lo cual de manera directa permite que se pueda obtener ese valor desde el puntero.
- Luego se imprime en pantalla dos informaciones. Primero se muestra la dirección a la que apuntan los punteros p1 y p2; que en la práctica es el lugar de la memoria donde están almacenado los datos de las variables num1 y num2. Esto lo notamos cuando se imprime el segundo bloque de texto, en la que se imprime los datos a los que apuntan p1 y p2, colocando como prefijo el asterisco en cada puntero.
- Como remate le paso la dirección de p2 a p1, y posteriormente le digo que p2 que apunte a la dirección 0 de la memoria.
Este ejemplo no trata de cumplir con un objetivo en particular, sino de mostrarles como se opera con punteros.
Espero les haya gustado. Nos vemos.
0 comentarios:
Publicar un comentario