Documenti di Didattica
Documenti di Professioni
Documenti di Cultura
INTRODUCCIN
Todo programa que est siendo ejecutado por la CPU, ocupa un bloque de bytes en la memoria RAM del
computador. Cuando un computador inicia su funcionamiento, el programa que toma el control es el Sistema
Operativo. El mismo ocupa las direcciones bajas de memoria, es decir un bloque de bytes que comienza en la
posicin 0. Normalmente estas direcciones tienen un tamao de 4 bytes (32bits) y se representan en Hexadecimal.
De tal manera que la memoria RAM a la que se puede acceder en forma directa por la CPU comienza en la posicin
00 00 00 00 y termina en FF FF FF FF.
Cuando un programa se prepara para ejecutarse, el sistema operativo le asigna un bloque de memoria RAM
que se divide en los siguientes sectores: CDIGO, DATOS, STACK (PILA), HEAP (MONTN o MONTCULO).
El sector CDIGO contiene las instrucciones del programa en lenguaje de mquina absoluto, el de DATOS
contendr las variables globales del programa, en el STACK se almacenar las variables locales y los parmetros por
valor de las funciones que utilice el programa, tambin se guarda all la direccin del cdigo donde deber retornar
cuando termine su ejecucin y en el HEAP, que suele ser mucho ms grande que los otros sectores, se utilizar para
almacenar estructuras de datos que requieren cambiar su tamao durante la ejecucin del programa y necesitan
gran cantidad de bloques de bytes.
Una distribucin tpica de estos sectores se representa en el siguiente esquema:
MEMORIA RAM
Los tipos de datos que se almacenan en el sector DATOS y en el STACK, ocupan un espacio fijo de memoria
que no puede cambiarse durante la ejecucin del programa. Por ello es que se llama estos tipos Estructuras
ESTTICAS, por ejemplo las variables simples tipo entero, real, lgicas o carcter, los arreglos, las cadenas, los
registros.
Por otro lado, los datos que se almacenan en el HEAP tienen como caracterstica que el espacio de memoria
ocupado por los mismos puede aumentar o disminuir durante la ejecucin del programa. De ah el nombre de
Estructuras DINMICAS, por ejemplo las listas, pilas, colas, rboles y grafos.
TIPO PUNTERO
Un tipo puntero representa la direccin de memoria de un bloque de byte correspondiente a una estructura de
datos cualquiera. Las variables de tipo puntero ocupan 4 bytes de memoria, de tal manera que pueden contener
valores en el rango hexadecimal de 00 00 00 00 a FF FF FF FF. Estas variables se alojan en el Sector DATOS o
STACK, segn donde se declaren, en forma esttica; pero, como veremos a continuacin, nos permitirn acceder al
Sector HEAP para manejar Estructuras Dinmicas de Datos.
En lenguaje C++ se declaran las variables punteros utilizando la siguiente sintaxis:
<tipo apuntado> *<identificador puntero>
Por ejemplo:
Char *pun_caracter; // puntero a un tipo char o caracter
int *pun_entero;
float *pun_real;
tipo_cadena *pun_cadena
tipo_vec_entero *pun_vec_entero
Si queremos saber la posicin de memoria donde comienza el bloque de bytes de alguna variable del
programa, podemos utilizar el operador de direccin &. Este operador va seguido del identificador de la variable
en cuestin.
Por otro lado, si queremos acceder a un bloque de bytes cuya posicin se encuentra almacenada en una
variable puntero, utilizamos el operador de indireccin *. Este operador antecede al identificador de la variable
puntero y hara las veces de identificador del bloque de bytes en cuestin.
Por ejemplo, supongamos que se declara una variable tipo entero var_entero y un puntero a dicho tipo
pun_entero, luego se puede asignar a este puntero la direccin de la variable tipo entero. De tal modo que
desde pun_entero se podra manipular el valor de var_entero, de la siguiente manera:
int var_entero, *pun_entero; pun_entero = &var_entero; *pun_entero = 135;
printf(El valor de la variable <var_entero> es %d, var_entero); // esto mostrara por pantalla el valor 135
En el caso de un puntero a estructura registro, existen dos formas de referenciar un campo del registro mediante el
puntero: (*pun_registro).campo1 o pun_registro->campo1
Veamos el siguiente ejemplo donde se podr apreciar cmo se va asignando la memoria para variables estticas de
distintos mbitos del programa (STACK o DATA) y en forma dinmica con punteros (HEAP):
#include <stdio.h>
#include <stdlib.h>
#include <conio.c>
int var_global0;
int var_global1;
ESTRUCTURA AUTOREFERENCIADA
Se llama estructura autoreferenciada a un registro que tiene al menos dos campos, en uno de los cuales se
almacena una informacin determinada y en el otro un puntero a la misma estructura. Se dice que esta definicin
es recursiva, ya que para declarar el tipo correspondiente a este registro se debe declarar un puntero al propio tipo
que se est declarando. Por ejemplo:
typedef char tipo_nombre[20];
struct dato_personal {tipo_nombre nombre; long dni; float salario; dato_personal *pun_dato_personal;};
Como vemos dentro de la declaracin del tipo dato_personal, hacemos referencia al propio tipo dato_personal
para declarar el campo pun_dato_personal.
Con esta clase de registros se pueden implementar estructuras dinmicas ms complejas, como ser: listas
enlazadas simples, listas enlazadas dobles, pilas y colas.
Nodo 1
Nodo 2
Nodo 3
Con las flechas representamos una direccin del HEAP y con el smbolo de toma tierra o masa representamos el
valor NULL.
Utilizando el ejemplo de la estructura anterior, el siguiente programa crea una lista con dos nodos y luego elimina
el primero de ellos, por cuestiones nemotcnicas llamaremos al campo puntero siguiente:
#include <stdio.h>
#include <stdlib.h>
#include <conio.c>
typedef char tipo_nombre[20];
struct dato_personal {tipo_nombre nombre; long dni; float salario; dato_personal *siguiente;};
main()
{ dato_personal *pun_dato,*pun_aux;
pun_dato = new(dato_personal);
//pun_dato = (dato_personal *) malloc(sizeof(dato_personal));
clrscr();
if (pun_dato!=NULL)
{ printf("Introducir los datos de una persona:\n");
printf("Nombre = "); gets(pun_dato->nombre);
printf("DNI = "); scanf("%ld", &pun_dato->dni);
printf("Salario = "); scanf("%f", &pun_dato->salario);
pun_dato->siguiente = new(dato_personal);
printf("\n\nEl primer registro ingresado es: %s , %ld , %.2f\nesta en la direccion %x del HEAP, ocupa %d
bytes\ny apunta al segundo registro\n\n", pun_dato->nombre, pun_dato->dni, pun_dato->salario, pun_dato,
sizeof(dato_personal));
if (pun_dato->siguiente!=NULL)
{ printf("Introducir los datos de otra persona:\n");
printf("Nombre = "); fflush(stdin); gets(pun_dato->siguiente->nombre);
printf("DNI = "); scanf("%ld", &pun_dato->siguiente->dni);
printf("Salario = "); scanf("%f", &pun_dato->siguiente->salario);
pun_dato->siguiente->siguiente = NULL;
printf("\n\nEl segundo registro ingresado es: %s , %ld , %.2f\nesta en la direccion %x del HEAP\ny no
apunta a ningun otro registro\n\n", pun_dato->siguiente->nombre, pun_dato->siguiente->dni,
pun_dato->siguiente->salario, pun_dato->siguiente);
pun_aux=pun_dato;
pun_dato=pun_dato->siguiente;
delete(pun_aux);
// free(pun_aux);
printf("Eliminamos el primer registro,\nahora el primer registro esta en la direccion %x", pun_dato);
}
else {printf("Error fatal en el HEAP"); exit(1);}
}
else {printf("Error fatal en el HEAP"); exit(1);}
getch();
}
Una estructura como sta solo puede recorrerse en forma secuencial, es decir que para acceder al nodo I debemos
pasar por los (I-1) nodos anteriores, siempre comenzando por el primer nodo. Consideremos el caso de una lista a
la que se ingresan N nodos y supongamos que queremos acceder al nodo I para mostrar su contenido, podemos
utilizar una variable puntero auxiliar para recorrer los nodos de la lista, de la siguiente manera:
#include <stdio.h>
#include <stdlib.h>
#include <conio.c>
typedef char tipo_nombre[20];
struct dato_personal {tipo_nombre nombre; long dni; float salario; dato_personal *siguiente;};
main()
{ dato_personal *pun_dato,*pun_aux;
int j,N,I;
clrscr();
printf("Introducir la cantidad de personas: "); scanf("%d",&N);
if (N==0) { pun_dato=NULL; printf("Lista vacia"); getch();}
else
{ pun_dato = new(dato_personal);
pun_aux = pun_dato;
j=0;
do
{
if (pun_aux!=NULL)
{ printf("Introducir los datos de una persona:\n");
printf("Nombre = "); fflush(stdin); gets(pun_aux->nombre);
printf("DNI = "); scanf("%ld", &pun_aux->dni);
printf("Salario = "); scanf("%f", &pun_aux->salario);
}
else {printf("Error fatal en el HEAP"); exit(1);}
j++;
if (j==N) {pun_aux->siguiente = NULL; break;}
pun_aux->siguiente = new(dato_personal);
pun_aux=pun_aux->siguiente;
}
while (true);
clrscr();
pun_aux=pun_dato;
j=0;
while (pun_aux != NULL)
{printf("El nodo %d tiene el siguiente contenido: %s , %ld , %.2f\n\n",
j, pun_aux->nombre, pun_aux->dni, pun_aux->salario);
pun_aux=pun_aux->siguiente;
j++;
}
clrscr();
do
{
printf("Introduzca nmero de nodo a ver o '0' para terminar:"); scanf("%d",&I);
if (I==0) break;
pun_aux = pun_dato;
j = 1;
while ((pun_aux != NULL) && (j < I)) {pun_aux = pun_aux->siguiente; j++; }
if (pun_aux != NULL) printf("El nodo %d tiene el siguiente contenido: %s , %ld , %.2f\n\n",
j, pun_aux->nombre, pun_aux->dni, pun_aux->salario);
else printf("La lista tiene %d nodos.\n\n", j-1 );
}
while (true);
}
}
Otro ejemplo es el caso de una lista ordenada por algn campo clave, en nuestro caso podra ser el campo dni.
Para manejar una lista como sta, se definen las siguientes operaciones bsicas:
Crear lista vaca.
Insertar un nodo de tal modo que la lista quede ordenada por el campo clave.
Eliminar, modificar o mostrar un nodo con un campo clave determinado.
Listar todos los nodos.
El siguiente programa realiza estas tareas mediante un men y la utilizacin de funciones para cada operacin:
#include <stdio.h>
#include <stdlib.h>
#include <conio.c>
typedef char tipo_nombre[20];
struct dato_personal {tipo_nombre nombre; long dni; float salario; dato_personal *siguiente;};
typedef dato_personal *tipo_pun_dato;
void crear_lista_vacia(tipo_pun_dato &pun)
{pun=NULL;}
aux->siguiente=NULL;
ant->siguiente=aux;
}
}
else {printf("Primer nodo:\n");
printf("Nombre = "); fflush(stdin); gets(aux->nombre);
printf("Salario = "); scanf("%f", &aux->salario);
aux->siguiente=pun;
pun=aux;
}
}
else printf("Error fatal en el HEAP no se puede insertar un nodo");
}
void eliminar_nodo(tipo_pun_dato &pun)
{if (pun!=NULL)
{
long dni;
tipo_pun_dato act, ant;
act=pun;
ant=pun;
printf("Introducir el DNI de la persona a eliminar:\n");
printf("DNI = "); scanf("%ld", &dni);
while ((act!=NULL) && (act->dni < dni)) {ant=act; act=act->siguiente;}
if ((act!=NULL) && (act->dni == dni))
{if (ant!=act)
ant->siguiente=act->siguiente;
else pun=act->siguiente;
delete(act);
printf("\nNodo eliminado\n");
}
else printf("\nNo existe un nodo con ese dni\n\n");
}
else printf("LISTA VACIA");
}
main()
{ tipo_pun_dato pun_dato;
int op;
crear_lista_vacia(pun_dato);
do
{
clrscr();
printf("
MENU\n");
printf("------------------------\n");
printf(" 1: INSERTAR\n");
printf(" 2: ELIMINAR\n");
printf(" 3: MODIFICAR\n");
printf(" 4: MOSTRAR\n");
printf(" 5: LISTAR\n");
printf(" 6: SALIR\n\n");
do
{ printf(" INTRODUCIR OPCION: ");
scanf("%d",&op);
if ((op>=1) && (op<=6)) break;
printf("Opcion no valida\n");
} while (true);
if (op==6) break;
clrscr();
switch (op)
{
case 1: insertar_nodo_ordenado(pun_dato); break;
case 2: eliminar_nodo(pun_dato); break;
case 3: modificar_nodo(pun_dato); break;
case 4: mostrar_nodo(pun_dato); break;
case 5: listar_nodos(pun_dato); break;
}
printf("\n\nPresione una tecla para continuar");
getch();
} while (true);
}
Por ltimo consideremos la posibilidad de guardar esta estructura en un archivo binario, de tal modo que quede
almacenada en forma permanente. Para ello definimos dos nuevas funciones, una para grabar la lista en un
archivo y otra para leer la lista desde ese archivo. Tambin se modific, por comodidad, la estructura del nodo.
De tal modo que contiene un campo registro que contiene la informacin propiamente dicha (nombre, dni, salario)
y otro campo para el puntero al siguiente nodo.
ant->siguiente=aux;
}
}
else {printf("Primer nodo:\n");
printf("Nombre = "); fflush(stdin); gets(aux->registro.nombre);
printf("Salario = "); scanf("%f", &aux->registro.salario);
aux->siguiente=pun;
pun=aux;
}
}
else printf("Error fatal en el HEAP no se puede insertar un nodo");
}
void eliminar_nodo(tipo_pun_dato &pun)
{if (pun!=NULL)
{
long dni;
tipo_pun_dato act, ant;
act=pun;
ant=pun;
printf("Introducir el DNI de la persona a eliminar:\n");
printf("DNI = "); scanf("%ld", &dni);
while ((act!=NULL) && (act->registro.dni < dni)) {ant=act; act=act->siguiente;}
if ((act!=NULL) && (act->registro.dni == dni))
{if (ant!=act)
ant->siguiente=act->siguiente;
else pun=act->siguiente;
delete(act);
printf("\nNodo eliminado\n");
}
else printf("\nNo existe un nodo con ese dni\n\n");
}
else printf("LISTA VACIA");
}
main()
{ tipo_pun_dato pun_dato;
int op;
tipo_nombre especifica_archi;
clrscr();
printf("Introducir la especificacion del archivo: ");
getch();
_flushall();gets(especifica_archi);
leer_lista_de_archivo(pun_dato,especifica_archi);
do
{
clrscr();
printf("
MENU\n");
printf("------------------------\n");
printf(" 1: INSERTAR\n");
printf(" 2: ELIMINAR\n");
printf(" 3: MODIFICAR\n");
printf(" 4: MOSTRAR\n");
printf(" 5: LISTAR\n");
printf(" 6: SALIR\n\n");
do
{ printf(" INTRODUCIR OPCION: ");
scanf("%d",&op);
if ((op>=1) && (op<=6)) break;
printf("Opcion no valida\n");
} while (true);
if (op==6) break;
clrscr();
switch (op)
{
case 1: insertar_nodo_ordenado(pun_dato); break;
case 2: eliminar_nodo(pun_dato); break;
case 3: modificar_nodo(pun_dato); break;
case 4: mostrar_nodo(pun_dato); break;
case 5: listar_nodos(pun_dato); break;
}
printf("\n\nPresione una tecla para continuar");
getch();
} while (true);
grabar_lista_en_archivo(pun_dato,especifica_archi);
}