[C++] Iniciación al manejo de los punteros

Iniciado por Horricreu, 10 Julio 2010, 00:04 AM

0 Miembros y 2 Visitantes están viendo este tema.

Horricreu

INICIACIÓN AL MANEJO DE LOS PUNTEROS




INTRODUCCIÓN

Debido al miedo general que provocan los punteros y, la mala información y explicación en la mayoría de talleres que corren por Internet e incluso en algunos libros, me dispongo a crear este pequeño texto intentando explicarlos lo mejor que pueda ya que cuesta mucho encontrar información decente.




MANEJANDO LOS PUNTEROS

¿Qué es un puntero? Un puntero es una variable que hace referencia a una dirección de memoria, como puede ser 0x000F212A, en hexadecimal. Un ejemplo es:

Código (cpp) [Seleccionar]
int a; //Declaración de una variable entera
int *ptr; //Declaración de un puntero


Pero es mejor así:

Código (cpp) [Seleccionar]
int a = 0; //Declaración de una variable entera
int *ptr = NULL; //Declaración de un puntero


Otros ejemplos:

Código (cpp) [Seleccionar]
char a = 0; //Declaración de una variable de caracteres
char *ptr = NULL; //Declaración de un puntero


Ya hemos visto la declaración de una variable normal y la de un puntero, pero nos preguntamos, ¿y por qué de la segunda manera? Al hacer esto, inicializamos las variables con un valor predeterminado en cambio de la primera forma las variables tienen un valor indeterminado y pueden contener, como llamamos en programación, basura. Recordemos que NULL es una constante declarada en los archivos de cabecera estándar y su valor es 0 o 0x00000000.

Antes de continuar, aclaremos dudas. Hemos visto que un puntero almacena una dirección de memoria como 0x000F212A o NULL y, también hemos dicho que si no sabemos a qué apuntar le asignamos NULL, pero claro, tener una dirección inválida es una estupidez, pero si hacemos por ejemplo:

Código (cpp) [Seleccionar]
a = 1;
ptr = &a;


El operador "&" obtiene la dirección de memoria de una variable cuando se aplica a esta. Por lo tanto:

a = 1 => 0x000F212A (imaginemos que tiene esta dirección de memoria).
ptr = &a => 0x000F212A (consecuentemente, aplicando este operador obtenemos la misma dirección de memoria).

Podríamos decir que ptr "apunta a" a. Ahora, para comprobar lo que acabamos de hacer:

Código (cpp) [Seleccionar]
std::cout << "a= " << a << std::endl;
std::cout << "ptr= " << *ptr << std::endl;


Lo que nos mostrará en pantalla será:

a= 1
ptr= 1


Vemos que sale lo mismo. El operador "*" aplicado a una variable de puntero obtiene el valor almacenado en una dirección de memoria indicada por este. Y además, podemos hacer lo siguiente:

Código (cpp) [Seleccionar]
*ptr = 2;

Estamos haciendo que el contenido apuntado por ptr almacene ahora: 2. O sea, como ptr apunta a a, es como hacer:

Código (cpp) [Seleccionar]
a = 2;

Luego, si hacemos:

Código (cpp) [Seleccionar]
std::cout << "a= " << a << std::endl;
std::cout << "ptr= " << *ptr << std::endl;


Lo que nos mostrará ahora en pantalla es:

a= 2
ptr= 2





ARREGLOS Y PUNTEROS

Un ejemplo de arreglo sería:

Código (cpp) [Seleccionar]
int x[4] = {11, 12, 13, 14};

Para este arreglo se le asigna una memoria contigua de la siguiente manera:



A x se le asigna la dirección de memoria del primer elemento del arreglo, en este caso: 11. Ahora vamos a crear un puntero al arreglo y a enseñar lo que pasa, jugando con el puntero:

Código (cpp) [Seleccionar]
int *ptr = NULL;
ptr = x;

std::cout << *ptr << std::endl;
ptr++;

std::cout << *ptr << std::endl;
ptr++;

std::cout << *ptr << std::endl;
ptr++;

std::cout << *ptr << std::endl;


Lo que nos mostrará en pantalla será:

11
12
13
14


Antes de empezar, una cosa importante: para que un puntero apunte a un arreglo, no hace falta que tenga el operador "&", ya que un arreglo es un puntero constante y por la misma razón no se puede tampoco incrementar un arreglo.

Bien, ptr apunta al primer elemento del arreglo, en el cual su dirección de memoria es 0x0012FF60. Lo mostramos por pantalla y, lógicamente como hemos puesto el operador "*" antes de ptr nos saldrá el contenido almacenado en esta posición de memoria: 11. Seguidamente, hacemos un postincremento al puntero, por lo tanto aumentará la dirección de memoria y apuntará al segundo elemento del arreglo; la dirección de memoria será 0X0012FF64, recordemos que un entero ocupa 4 bytes. Luego mostramos el contenido de esta dirección de memoria: 12. Hacemos esto hasta llegar al último elemento.




MEMORIA DINÁMICA

Comprendiendo todo lo explicado hasta ahora nos sirve para comprender el funcionamiento y las características más básicas de los punteros, ahora bien, entrando en el sector de la memoria dinámica, profundizamos y obtenemos una mayor potencia de estos mismos. Recordemos que se crea un puntero de la siguiente manera:

Código (cpp) [Seleccionar]
int *ptr = NULL;

Recordemos también, que al hacer esto el puntero apunta a una dirección inválida de memoria por lo que hacer:

Código (cpp) [Seleccionar]
*ptr = 1;

Sería inválido. Si queremos reservar memora exclusivamente para que nuestro puntero apunte a algo lo hacemos con el operador new. Por ejemplo:

Código (cpp) [Seleccionar]
int *ptr = new int;

Con el operador new reservamos memoria dinámica y, se le pasa como operando el tipo de variable a reservar. Luego de esto, se retorna la dirección de esta memoria reservada, por lo que necesitamos obligatoriamente un puntero para acceder a esta memoria.

Pero, al igual que reservamos memoria dinámica, también la podemos borrar o destruir. Y es obligatorio borrar toda esta memoria reservada ya que si no podemos ocasionar un agotamiento de memoria. Por ejemplo:

Código (cpp) [Seleccionar]
int *ptr = new int;
delete ptr;


Reservamos memoria exclusivamente para el puntero con el operador new y, la eliminamos al final con el operador delete (el nombre del puntero es el que debe acompañar a delete).




ARREGLOS DINÁMICOS

Ya hemos visto el funcionamiento de la memoria dinámica y cómo utilizarla pero, aún podemos sacarle más jugo al tema. La aplicaremos a los arreglos. Un ejemplo de arreglo utilizando dinámico:

Código (cpp) [Seleccionar]
int *ptr = new int[5];

En este caso, reservamos cinco celdas consecutivas, o sea un arreglo y, cada de una de ellas tiene la memoria de un entero, 4 bytes.

Código (cpp) [Seleccionar]
for(int n = 0; n < 5; n++) std::cout << ptr[n] << std::endl;

Así ya accedemos a los elementos del arreglo.

La ventaja de utilizar arreglos dinámicos es que puedes establecer su tamaño en tiempo de ejecución, justo al contrario que los arreglos normales. Para establecer el tamaño de un arreglo dinámico, se puede hacer por ejemplo:

Código (cpp) [Seleccionar]
int n = 0;

std::cin >> n;

int *ptr = new int[n];


De la misma forma que se puede eliminar la memoria dinámica normal, también se puede eliminar los arreglos dinámicos. Un ejemplo sería:

Código (cpp) [Seleccionar]
int *ptr = new int[5];
delete[] ptr;


La única diferencia con el delete de antes es que sólo se le añaden estos signos "[]" después del delete.




DESPEDIDA

Aquí termina este escrito y, espero que me haya explicado bien y que os haya servido para comprender un poquito más los punteros. Podéis contactar conmigo para cualquier duda que tengáis o simplemente, comentarlo acá.

Saludos :P

leogtz

Estaría bien que trataras más la aritmética de punteros.

Me gustó, continúa así.
Código (perl) [Seleccionar]

(( 1 / 0 )) &> /dev/null || {
echo -e "stderrrrrrrrrrrrrrrrrrr";
}

http://leonardogtzr.wordpress.com/
leogutierrezramirez@gmail.com

Horricreu

Cita de: Leo Gutiérrez. en 10 Julio 2010, 00:14 AM
Estaría bien que trataras más la aritmética de punteros.

Me gustó, continúa así.

¡Sí! Justamente lo pensaba, a ver si mañana me animo y escribo ejemplos de aritmética de punteros ;)

Saludos :P

isseu


[Zero]

Está francamente bien, hace una semana no te veía capaz de escribir algo así, pero me alegro de equivocarme, ojalá lo siga haciendo  ;).

Saludos

"El Hombre, en su orgullo, creó a Dios a su imagen y semejanza.”
Nietzsche

@synthesize

#5
Está de 10, como siempre con tus post, solo una pequeña pega, según mi gusto, te has limitado a contar, a tu forma, lo que ya dicen todos los libros y manuales que he visto, y me imagino que la mayoría de los que no he visto. Si intentaras enfocar esto a cosas mas "nuevas" (Sé que es imposible hacer cosas nuevas con esto, pero es para que se me entienda) ya sería mas que perfecto.

Un saludo

Pd. estaría muy bien en la WIki  ;)

Horricreu

#6
Cita de: [Zero] en 10 Julio 2010, 00:19 AM
Está francamente bien, hace una semana no te veía capaz de escribir algo así, pero me alegro de equivocarme, ojalá lo siga haciendo  ;).

Saludos

:o ¡Muchas gracias, de verdad! Me vienen ganas de escribir y aprender más :)

Saludos :P

Cita de: Daemon Freedom en 10 Julio 2010, 00:22 AM
Está de 10, como siempre con tus post, solo una pequeña pega, según mi gusto, te has limitado a contar, a tu forma, lo que ya dicen todos los libros y manuales que he visto, y me imagino que la mayoría de los que no he visto. Si intentaras enfocar esto a cosas mas "nuevas" (Sé que es imposible hacer cosas nuevas con esto, pero es para que se me entienda) ya sería mas que perfecto.

Un saludo

Te juro que hace unas pocas semanas no sabía que era un puntero (estoy exagerando). Leí, leí y leí libros y manuales y, en ninguno me quedaba claro... a ver si con esto queda todo bien clarito. Verás que me repito mucho, para que quede muy bien explicado :)

Y sí, tienes razón, quizás se tiene que cambiar el método de aprendizaje y no ir al método clásico ;) Gracias por tu crítica :)

Saludos :P

@synthesize

Cita de: Horricreu en 10 Julio 2010, 00:26 AM
Cita de: Daemon Freedom en 10 Julio 2010, 00:22 AM
Está de 10, como siempre con tus post, solo una pequeña pega, según mi gusto, te has limitado a contar, a tu forma, lo que ya dicen todos los libros y manuales que he visto, y me imagino que la mayoría de los que no he visto. Si intentaras enfocar esto a cosas mas "nuevas" (Sé que es imposible hacer cosas nuevas con esto, pero es para que se me entienda) ya sería mas que perfecto.

Un saludo

Te juro que hace unas pocas semanas no sabía que era un puntero (estoy exagerando). Leí, leí y leí libros y manuales y, en ninguno me quedaba claro... a ver si con esto queda todo bien clarito. Verás que me repito mucho, para que quede muy bien explicado :)

Y sí, tienes razón, quizás se tiene que cambiar el método de aprendizaje y no ir al método clásico ;) Gracias por tu crítica :)

Saludos :P

Si me das permiso colaboro un poco contigo, que hace tiempo que dejé atrás punteros y al verdad, estoy muy puteado por no recordarlos...  :-\

Horricreu

Cita de: Daemon Freedom en 10 Julio 2010, 00:28 AM
Cita de: Horricreu en 10 Julio 2010, 00:26 AM
Cita de: Daemon Freedom en 10 Julio 2010, 00:22 AM
Está de 10, como siempre con tus post, solo una pequeña pega, según mi gusto, te has limitado a contar, a tu forma, lo que ya dicen todos los libros y manuales que he visto, y me imagino que la mayoría de los que no he visto. Si intentaras enfocar esto a cosas mas "nuevas" (Sé que es imposible hacer cosas nuevas con esto, pero es para que se me entienda) ya sería mas que perfecto.

Un saludo

Te juro que hace unas pocas semanas no sabía que era un puntero (estoy exagerando). Leí, leí y leí libros y manuales y, en ninguno me quedaba claro... a ver si con esto queda todo bien clarito. Verás que me repito mucho, para que quede muy bien explicado :)

Y sí, tienes razón, quizás se tiene que cambiar el método de aprendizaje y no ir al método clásico ;) Gracias por tu crítica :)

Saludos :P

Si me das permiso colaboro un poco contigo, que hace tiempo que dejé atrás punteros y al verdad, estoy muy puteado por no recordarlos...  :-\

Como quieras ;) Ponte en contacto conmigo a través del MSN (está en mi perfil).

Saludos :P

Novlucker

Cita de: Daemon Freedom en 10 Julio 2010, 00:22 AM... te has limitado a contar, a tu forma, lo que ya dicen todos los libros y manuales que he visto, y me imagino que la mayoría de los que no he visto ...

No obstante, conozco a varios con dolores de cabeza cuando recién comienzan con los punteros, y que lo explique así de claro alguien que recién lo entiende tiene su mérito, además de que puede resultar más sencillo para otros que comienzan al igual que él :)

Saludos
Contribuye con la limpieza del foro, reporta los "casos perdidos" a un MOD XD

"Hay dos cosas infinitas: el Universo y la estupidez  humana. Y de la primera no estoy muy seguro."
Albert Einstein