Punteros

Memoria

Es un dispositivo donde se almacena los datos de algun programa para que estos sean fácilmente accedidos por el procesador. Este almacenamiento no es permanente, cada vez que apaguemos nuestro equipo, la información en memoria se pierde.

Dirección de memoria

Observando a la memoria desde una mayor abstracción, podríamos imaginarla como si de un gran conjunto de celdas se tratara, cada una de estas celda estaría marcada por un número que la identifica y es este numero al que podríamos referirnos como dirección de memoria. Si tenemos la dirección de memoria, podemos ubicar fácilmente la celda y el contenido.

Tipos de memoria

Las memoria se clasifica en tres tipos diferentes que decidirán el tiempo en que una variable permanece en ellas.

  • Memoria estática o global - Este tipo de memoria estarán aquellas variables cuyo tiempo de vida corresponde con el del programa, es decir, estas variables aparecerán en memoria al iniciar el programa y ahí permanecerán hasta que termine.
  • Memoria automática - Las variable de esta región están atadas al tiempo de vida de la función donde son declaradas. Cuando se llama a una función las variables que en ella se encuentra toman su lugar en memoria y ahí permanecen hasta que la función termina.
  • Memoria dinámica - Existe una región de la memoria que se le conoce como heap, aquí reside memoria que puede ser tomada por tu programa en tiempo de ejecución. Las variables que resida en algún espacio que fue tomado de esta manera, permanecerán ahí hasta que el programador dedica liberar dicho espacio.
Tipo de memoria Tiempo de vida
Global \ Estática Durante todo el tiempo de vida del programa.
Automática Durante la ejecución de la funcion donde son declaradas.
Dinámica Hasta que el programador decida liberar el espacio.

Punteros

Un puntero (pointer) es una variable el cual almacena sólo la dirección en memoria donde se está guardando el valor de otra variable. Nos permite guardar la dirección de memoria donde se localiza alguna otra variable o constante. Los punteros son elementales para la creación de estructuras de datos más complejas.

#include <iostream>

using namespace std;

int main() 
{
    int num;

    num = 20;

    cout << "Number: " << num << endl;
    cout << "Mem address: " << &num << endl;
}
//-> Number: 20
//-> Mem address: 0x7fff4da78464

Cuando vemos el símbolo de & antes de una variable, nos estamos refiriendo a la dirección de memoria o donde se encuentra la variable. Ahora vamos a crear un ejemplo de cómo creamos un puntero, asignamos dirección de otra variable y mostramos su contenido usando el símbolo de * antes del nombre del puntero.

#include <iostream>

using namespace std;

int main() 
{
    int num, *dir_num;
    num = 20;

    dir_num = &num;

    cout << "Number: " << *dir_num << endl;
    cout << "Mem address: " << &dir_num << endl;
}
//-> Number: 20
//-> Mem address: 0x7ffff1ad7bb0

La asignación de punteros tiene que ser a punteros definidos del mismo tipo.

Definición de punteros

Para definir un puntero utilizamos la siguiente reglas:

type *name;

El asterisco es un operador unario el cual es requerido por el compilador para identificar que estas definiendo una variable puntero. Sin este símbolo antes de la variable, el compilador sólo entenderá que estas declarando una variable regular.

#include <iostream>

using namespace std;

int main() 
{
    int *number;
    char *character;
    double *decimal;
}

Que es el operador Address-of &

El símbolo de ampersand es utilizado para obtener la dirección de la memoria en donde se encuentre guardada el valor de cualquier variable. Esto puede hacerse utilizando el símbolo antes de la variable que deseamos obtener la dirección.

#include <iostream>

using namespace std;

int main() 
{
    int number = 20;
    char character = 'c';
    double decimal = 3.14;

    cout << "Number mem address: " << &number << endl;
    cout << "Character mem address: " << &character << endl;
    cout << "Decimal mem address: " << &decimal << endl;
}
//-> Number mem address: 0x7ffcc24623ec
//-> Character mem address: c
//-> Decimal mem address: 0x7ffcc24623f0

Obtener el valor del puntero

También usando el símbolo de asterisco y la variable puntero podemos obtener el valor el cual se encuentre el puntero haciendo referencia a la memoria.

#include <iostream>

using namespace std;

int main() 
{
    int num, *dir_num;
    num = 20;

    dir_num = &num;

    cout << "Number: " << *dir_num << endl;
    cout << "Mem address: " << &dir_num << endl;
}
//-> Number: 20
//-> Mem address: 0x7ffff1ad7bb0

Puntero aritmética

La dirección el cual guardamos en nuestros punteros son valores numéricos, podemos realizar operaciones aritmética con estos valores, los punteros solo nos permite realizar operaciones aritméticas tales como suma o resta.

Imaginemos que tenemos 3 variables val1, val2, val3 de tipo double y que cada una de estas se encuentra en direcciones contigua de memoria. Ahora creamos un puntero y le asignamos la dirección de la primera variable.

double val1, val2, val3;

double *ptr_double = &val1;

Supongamos que la dirección de nuestra primera variable es 200, si añadimos a una unidad a esta dirección, lógicamente pensamos que el resultado sería 201, pero no es así, al sumarle una unidad, el resultado será 208. Todo esto tienen que ver con el tipo de dato al que nos estamos refiriendo. Cada tipo de dato hace referencia al espacio que se ocupa en memoria por tanto si agregamos a un apuntador n unidades, el resultado será anadi dicho número n multiplicado por el tamaño de byte que ocupa el tipo de datos al que estamos apuntando. Ejemplo basado a una dirección con valor inicial 200:

  • char = 1 byte. A la dirección se le sumará 1 unidad * 1 byte, resultado será 201
  • short = 2 bytes. A la dirección se le sumará 1 unidad * 2 bytes, resultado será 202
  • int = 4 bytes. A la dirección se le sumará 1 unidad * 4 bytes, resultado será 204

En lo que respecta a la resta es precisamente lo mismo. Es importante entender que un puntero puede sumar o restar su tamaño solo si los tipos de variable al cual se apunta son el mismo tipo. También podemos realizar comparaciones usando operadores mayor que, menor que, igual que, etc. Son muy poco utilizados pero vale la pena mencionar su existencia.

Puntero Nulo

Los puntero están diseñados a guardar direcciones aunque no exista algún valor o dirección, muchas veces podemos tener conflicto al crear punteros el cual no tienen una dirección de inicio pero luego se usará, para estos casos debemos asignar el valor nulo a nuestro puntero.

#include <iostream>

using namespace std;

int main() 
{
    int *dir_num = NULL;
    int num = 20;
    dir_num = &num;
    cout << "Mem address: " << *dir_num << endl;
}
//-> Number: 20

results matching ""

    No results matching ""