El tipo puntero
Un puntero es una variable que apunta o referencia a una ubicacin de
memoria en la cual hay datos. El contenido del puntero es la direccin de esa
ubicacin. Para entender qu es un puntero veremos primero cmo se
almacenan los datos en un computador. La memoria de un computador est
compuesta por unidades bsicas llamadas bits. Cada bit slo puede tomar dos
valores, normalmente denominados alto y bajo, 1 y 0. Pero trabajar con bits
no es prctico, y por eso se agrupan. Cada grupo de 8 bits forma un byte u
octeto. En realidad el microprocesador, y por lo tanto nuestro programa, slo
puede manejar directamente bytes o grupos de dos o cuatro bytes. Para
acceder a los bits hay que acceder antes a los bytes.
Cada byte de la memoria de un computador tiene una direccin, llamada
direccin de memoria.
Los microprocesadores trabajan con una unidad bsica de informacin, a la
que se denomina palabra (en ingls Word). Dependiendo del tipo de
microprocesador una palabra puede estar compuesta por uno, dos, cuatro,
ocho o diecisis bytes. Hablaremos en estos casos de plataformas de 8, 16, 32,
64 128 bits. Se habla indistintamente de direcciones de memoria, aunque las
palabras sean de distinta longitud. Cada direccin de memoria contiene
siempre un byte. Lo que suceder cuando las palabras sean, por ejemplo, de
32 bits es que accederemos a posiciones de memoria que sern mltiplos de 4.
Por otra parte, la mayor parte de los objetos que usamos en nuestros
programas no caben en una direccin de memoria. La solucin utilizada para
manejar objetos que ocupen ms de un byte es usar posiciones de memoria
correlativas. De este modo, la direccin de un objeto es la direccin de
memoria de la primera posicin que contiene ese objeto. Dicho de otro modo,
si para almacenar un objeto se precisan cuatro bytes, y la direccin de
memoria de la primera posicin es n, el objeto ocupar las posiciones desde n
a n+3, y la direccin del objeto ser, tambin, n.
Todo esto sucede en el interior de la mquina, y nos importa relativamente
poco. Pero podemos saber qu tipo de plataforma estamos usando
averiguando el tamao del tipo int, y para ello hay que usar el operador
sizeof, por ejemplo:
cout << "Plataforma de " << 8*sizeof(int) << " bits";
AERS..Pg. 1
array = ARREGLO
AERS..Pg. 2
AERS..Pg. 3
Ejemplos:
int *pEntero;
char *pCaracter;
struct stPunto *pPunto;
Con el (*) junto al tipo, en lugar de junto al identificador del objeto, tambin
est permitida. De hecho, tambin es legal la forma:
<tipo> * <identificador>;
Equivale a:
int* pEntero;
Otro detalle importante es que, aunque las tres formas de situar el asterisco en
la declaracin son equivalentes, algunas de ellas pueden inducirnos a error,
sobre todo si se declaran varios objetos en la misma lnea:
int* x, y;
AERS..Pg. 4
Los punteros apuntan a objetos, por lo tanto, lo primero que tenemos que
saber hacer con nuestros punteros es asignarles direcciones de memoria
vlidas de objetos. Para averiguar la direccin de memoria de cualquier objeto
usaremos el operador de direccin (&), que leeremos como "direccin de".
Por supuesto, los tipos tienen que ser "compatibles", no podemos almacenar la
direccin de un objeto de tipo char en un puntero de tipo int.
Por ejemplo:
int A;
int *pA;
pA = &A;
En (1) asignamos al objeto apuntado por pEntero en valor del objeto x. Como
pEntero apunta al objeto y, esta sentencia equivale (segn la secuencia del
programa), a asignar a y el valor de x.
AERS..Pg. 5
NULL es una constante, que est definida como cero en varios ficheros de
cabecera, como "cstdio" o "iostream", y normalmente vale 0L. Sin embargo,
hay muchos textos que recomiendan usar el valor 0 para asignar a punteros
nulos, al menos en C++.
Correspondencia entre arrays y punteros
En muchos aspectos, existe una equivalencia entre arrays y punteros. De
hecho, cuando declaramos un array estamos haciendo varias cosas a la vez:
Declaramos un puntero del mismo tipo que los elementos del array.
AERS..Pg. 6
Reservamos memoria para todos los elementos del array. Los elementos
de un array se almacenan internamente en la memoria del ordenador en
posiciones consecutivas.
Ejemplo:
int vector[10];
int *puntero;
puntero = vector; /* Equivale a puntero = &vector[0]; (1)
esto se lee como "direccin del primer elemento de
vector" */
(*puntero)++;
/* Equivale a vector[0]++; (2) */
puntero++;
// puntero equivale a asignar a puntero=puntero+1 (3)
AERS..Pg. 7
AERS..Pg. 8
Punteros genricos
Es posible declarar punteros sin especificar a qu tipo de objeto apuntan:
void *<identificador>;
AERS..Pg. 9
Por ejemplo:
#include <iostream>
using namespace std;
int main() {
char cadena[10] = "Hola";
char *c;
int *n;
void *v;
c = cadena; // c apunta a cadena
n = (int *)cadena; // n tambin apunta a cadena
v = (void *)cadena; // v tambin
cout << "carcter: " << *c << endl;
cout << "entero:
" << *n << endl;
cout << "float:
" << *(float *)v << endl;
return 0;
}
El resultado ser:
carcter: H
entero:
1634496328
float:
2.72591e+20
AERS..Pg. 10
<<
<<
<<
<<
<<
<<
return 0;
}
Ejemplos
Veamos algunos ejemplos de cmo trabajan los punteros.
Primero un ejemplo que ilustra la diferencia entre un array y un puntero:
#include <iostream>
using namespace std;
int main() {
char cadena1[] = "Cadena 1";
char *cadena2 = "Cadena 2";
cout << cadena1 << endl;
cout << cadena2 << endl;
//cadena1++; // Ilegal, cadena1 es constante
cadena2++; // Legal, cadena2 es un puntero
cout << cadena1 << endl;
cout << cadena2 << endl;
cout << cadena1[1] << endl;
cout << cadena2[0] << endl;
cout << cadena1 + 2 << endl;
cout << cadena2 + 1 << endl;
cout << *(cadena1 + 2) << endl;
cout << *(cadena2 + 1) << endl;
return 0;
}
AERS..Pg. 11
AERS..Pg. 12
AERS..Pg. 13
Liberacin de memoria
Hay una regla de oro cuando se usa memoria dinmica: toda la memoria que
se reserve durante el programa hay que liberarla antes de salir del programa.
No seguir esta regla es una actitud muy irresponsable, y en la mayor parte de
los casos tiene consecuencias desastrosas. No confes de lo que diga el
compilador, de que estas variables se liberan solas al terminar el programa, no
siempre es verdad.
Ejemplo:
int main()
{
int *a;
a = new int; // variable dinmica
*a = 10;
a = new int; // nueva variable dinmica,
// se pierde el puntero a la anterior
*a = 20;
AERS..Pg. 14
}
De la misma forma que manejamos vectores tambin se pueden
manejar tablas o matrices:
Veamos la declaracin de un puntero a puntero:
int **tabla;
Tabla es un puntero que apunta a un objeto que a su vez es puntero de un
entero.
AERS..Pg. 15
Cada elemento del arreglo anterior puede ser a su vez un puntero al primer
elemento de otro arreglo:
int m = 231;
for(int i = 0; i < n; i++)
tabla[i] = new int[m];
Una de las grandes diferencias con los arreglos normales es que antes de
finalizar el programa es necesario liberar la memoria dinmica usada, primero
la asociada a cada elemento de Tabla[i]:
for(int i = 0; i < n; i++) delete[] tabla[i];
AERS..Pg. 16
AERS..Pg. 17
Listas Enlazadas
Una de las aplicaciones ms interesantes y potentes de la memoria dinmica y
de los punteros son, sin duda, las estructuras dinmicas de datos. Las
estructuras bsicas disponibles en C y C++ (structs y arrays) tienen una
importante limitacin: no pueden cambiar de tamao durante la ejecucin. Los
arrays estn compuestos por un determinado nmero de elementos, nmero
que se decide en la fase de diseo, antes de que el programa ejecutable sea
creado.
AERS..Pg. 18
Definicin:
La forma ms simple de estructura dinmica es la lista abierta. En esta
forma los nodos se organizan de modo que cada uno apunta al siguiente,
y el ltimo no apunta a nada, es decir, el puntero del nodo siguiente vale
NULL.
En las listas abiertas existe un nodo especial: el primero. Normalmente
diremos que nuestra lista es un puntero a ese primer nodo y llamaremos
a ese nodo la cabecera de la lista. Eso es porque mediante ese nico
puntero podemos acceder a toda la lista.
AERS..Pg. 19
Es muy importante que nuestro programa nunca pierda el valor del puntero
al primer elemento, ya que si no existe ninguna copia de ese valor, y se
pierde, ser imposible acceder al nodo y no podremos liberar el espacio de
memoria que ocupa.
AERS..Pg. 20
Cada una de estas operaciones tendr varios casos especiales, por ejemplo, no
ser lo mismo insertar un nodo en una lista vaca, o al principio de una lista no
vaca, o la final, o en una posicin intermedia.
Creacin de una lista enlazada
El procedimiento de creacin de una lista enlazada es muy simple e inicializa
un puntero del tipo TLista a NULL. Por ejemplo podra ser:
AERS..Pg. 21
AERS..Pg. 22
El proceso es el siguiente:
1. Necesitamos un puntero que seale al ltimo elemento de la lista. La
manera de conseguirlo es empezar por el primero y avanzar hasta que
el nodo que tenga como siguiente el valor NULL.
2. Hacer que nodo->siguiente sea NULL.
3. Hacer que ultimo->siguiente sea nodo.
El Algoritmo es el siguiente:
FIN InsertarAlFinal
***********************************
void INSERTAR_FIN(pNodo &Lista, DATO Elem)
{
pNodo A = Lista, NUEVO;
NUEVO = new (tipoNodo);
NUEVO->Dato = Elem;
NUEVO->Sig = NULL;
if(Lista == NULL)
Lista = NUEVO;
else
{while (A->Sig != NULL) A=A->Sig;
A->Sig = NUEVO;
}
}
Suponemos que ya disponemos del nuevo nodo a insertar, apuntado por nodo,
y un puntero al nodo a continuacin del que lo insertaremos.
El proceso a seguir ser:
1. Hacer que nodo->siguiente seale a anterior->siguiente.
2. Hacer que anterior->siguiente seale a nodo.
AERS..Pg. 24
AERS..Pg. 25
pNodo A=Lista;
if(Lista!=NULL)
{
}
else
{
Por ejemplo, para mostrar todos los valores de los nodos de una lista,
podemos usar el siguiente bucle en C:
typedef struct _nodo {
int dato;
AERS..Pg. 26
Supongamos que slo queremos mostrar los valores hasta que encontremos
uno que sea mayor que 100, podemos sustituir el bucle por:
indice = Lista;
while(indice && indice->dato <= 100) {
cout<< indice->dato<< "\n";
indice = indice->siguiente;
}
AERS..Pg. 27
AERS..Pg. 28
**************************************
void Eiminarprimero(Lista &lista, Tdato & Elem )
{
pNodo
if
nodo;
lista!=NULL
{nodo = lista;
lista = lista->Sig;
Elem=nodo->Dato;
delete nodo;
INICIO
SI lista != NULO ENTONCES // Sino, no hay que hacer nada
ptr = lista
MIENTRAS ptr != NULO Y ptr->Dato != Elem HACER
ant = ptr
ptr = ptr->sig
FINMIENTRAS
SI ptr != NULO ENTONCES // Encontrado
SI ant == NULO ENTONCES // Es el primer elemento
lista = lista->sig
SINO
ant->sig = ptr->sig
FINSI
LIBERAR(ptr)
FINSI
FINSI
FIN Borrar
*************************************************************
void Borrar(Lista &lista, int v)
{
pNodo anterior, nodo;
nodo = lista;
anterior = NULL;
while(nodo && nodo->dato != v)
{ anterior = nodo;
nodo = nodo->Sig;
}
if(!nodo || nodo->dato != v)
return;
else {
if(!anterior) // Primer elemento
lista = nodo->Sig;
else // un elemento cualquiera
anterior->Sig = nodo->Sig;
delete nodo;
}
}
AERS..Pg. 30
AERS..Pg. 31
return NULL;
}
}
pNodo A=Lista;
if(Lista==NULL )
return NULL;
else
{while(A->Sig!=NULL) A=A->Sig;
return A;}}
AERS..Pg. 32
pNodo nodo;
while(lista)
{
nodo = lista;
lista = lista->Sig;
delete nodo;
}
}
Ejercicio de aplicacin.
Se crea una lista de n elementos y la llena con valores aleatorios usando una
funcin random(). Posteriormente entra en un ciclo donde se insertan
elementos en posiciones especficas de la lista, mostrando en cada paso el
resultado de cada operacin.
En el programa se define una funcin que permite insertar un nodo en una
posicin especfica de la lista. La funcin (INSERTAR_NODO) recibe como
parmetros la direccin de la lista, el elemento a insertar y la posicin donde
se debe insertar.
El programa no verifica si una posicin especifica esta fuera del alcance de la
lista, por ejemplo, si se tiene una lista de 5 elementos y se intenta un
elemento en la posicin 8.
#include<iostream.h>
#include<iomanip.h>
#include<conio.h>
#include<stdlib.h>
typedef int tipo_info;
struct t_nodo
{ tipo_info Info;
t_nodo * Sig;
};
typedef struct t_nodo tipo_nodo;
typedef tipo_nodo* pt_nodo;
void INSERTAR_INI(pt_nodo &L,
tipo_info Elem)
{ pt_nodo Aux;
Aux=new(tipo_nodo);
Aux->Info=Elem;
Aux->Sig=NULL;
if(L==NULL) L=Aux;
else
{ Aux->Sig=L;
L=Aux;
}
}
void INSERTAR_NODO(pt_nodo &Lista,
tipo_info Elem, int ind)
{ int I;
pt_nodo Act;
Act=Lista;
if(ind==1)
INSERTAR_INI(Lista,Elem);
AERS..Pg. 33
EJERCICIOS PROPUESTOS
1. Escribir una funcin que cuente (CONTAR(Lista, valor)) el nmero de veces que
aparece un valor en una lista, modificando la funcin que determina si un dato
existe o no en la lista.
2. Desarrolle una funcin que permita intercambiar dos nodos de una lista
simplemente enlazada dadas las posiciones de los mismos.
3. Escribir una funcin que permita eliminar el penltimo Nodo de una Lista Enlazada.
4. Escribir una funcin que permita detectar si una lista tiene valores repetidos.
5. Escribir una funcin que recibe dos listas L1 y L2, remueve el nodo frontal de la
segunda lista y lo inserta al inicio de la primera lista.
6. Escribir una funcin DivAlterna() que divida una lista en dos sub-listas, pero las
listas pequeas deben ser construidas tomando en forma alterna los nodos de la
lista original. Si la lista original fuera {7,10,12,8,17}, se obtienen dos listas
{7,12,17} y {10,8}.
7. Escribir un algoritmo que permita sumar dos polinomios P1 y P2 almacenados en
listas simples enlazadas.
8. Desarrolle un programa que lleve el registro de todos los estudiantes de la
asignatura computacin III, utilizando listas enlazadas. Los estudiantes aprobados
deben insertarse por el principio de la lista y los reprobados por el final. Los datos
requeridos por cada estudiante son: cedula, nombre y nota definitiva. El programa
debe permitir realizar las operaciones de: agregar estudiante, buscar estudiante
por cedula, eliminar estudiante, total estudiantes aprobados, total estudiantes
reprobados.
9. Disear un programa que maneje N cantidad de datos de tipo entero en dos listas
con insercin por el principio. Debe existir un ciclo de carga para la primera lista y
otro para segunda. Una vez cargados los datos en ambas listas, el programa debe
comparar las dos listas para verificar si ambas listas son iguales en tamao y
contenido. Una vez realizada la verificacin, el programa debe mostrar: si las listas
son iguales en tamao y contenido, si las listas son iguales en tamao pero; no en
contenido, o no tienen ni el mismo tamao ni el mismo contenido.
10. Crear una funcin que almacene N cantidad de datos de tipo real en una lista. Una
vez cargados los datos en la lista, la funcin debe calcular el promedio de todos los
datos. Posteriormente, debe cargar los datos menores o igual al promedio en una
segunda lista, y los mayores en una tercera lista. Al finalizar este proceso, la
funcin debe mostrar lo siguiente: a) Los datos cargados en la lista principal, b) El
promedio, c) Los datos cuyo valor sea igual o menor al promedio d) los datos que
sean mayores al promedio.
AERS..Pg. 34
12. Disear un programa que permita: pasar los datos de un archivo lista_mus2.txt
a una lista enlazada, mostrar los datos cargados en la lista y permitir buscar los
datos de un intrprete en particular. Donde el campo Datos de cada nodo estara
conformado por los siguientes campos: fecha, hora, tamao del archivo (mp3),
intrprete y titulo.
CLIENTES
CUENTA CEDULA
100
7777
101
1234
102
9876
200
7777
:
OPERACIONES
CUENTA COD MONTO
100
D
20000
100
R
15000
200
R
12000
101
D
30000
100
R
10000
:
AERS..Pg. 35