1.  Problemas genéricos al utilizar punteros

1.1  No reservar memoria para un nuevo nodo

var nuevo : ^Integer;
begin
	{ new(nuevo); }
	nuevo^ := 1;
end.
  • ERROR: La variable nuevo no tenía memoria reservada. Antes de desreferenciar un puntero, el mismo debe estar apuntando a un lugar de memoria que haya sido reservado previamente utilizando el procedimiento new.

1.2  Asignar NIL luego de reservar memoria

var nuevo : ^Integer;
begin
	new(nuevo);
	nuevo := NIL; { ! }
	nuevo^ := 1;
end.
  • ERROR: El asignar NIL luego de invocar a new no solo hace que el llamado a new pierda sentido sino que también deja un lugar de memoria desperdiciado que es imposible de acceder para liberarlo.

1.3  Inicializar punteros utilizando new para luego asignarlo

var it, nuevo : ^Integer;
begin
	new(it);
	it^=1;

	new(nuevo); { ! }
	nuevo := it;
end;
  • ERROR: Este es un caso más general del invocar a new y luego asignar NIL. Solo es necesario invocar a new si se necesita generar un nuevo nodo. En otro caso no se debe invocar a new porque sino se reservará un espacio de memoria que luego será inaccesible.

1.4  Creer que una asignación ya genera un alias

var l1, l2 : ^Integer;
begin
	l1 := NIL;
	l2 := l1; { ! }
	new(l1);
	l1^ := 1;
	if (l1 = l2) then
		writeLn('l1 y l2 son iguales')
	else
		writeLn('l1 y l2 NO son iguales')
end;
  • El programa imprime el mensaje de la sentencia 'else' por lo que l1 y l2 son distintos. Quien hubiera esperado que sean iguales debe tener en cuenta que el procedimiento new modifica el puntero parámetro, por lo que 'l1' pasa a apuntar al lugar de memoria que se reserva mientras que 'l2' sigue valiendo NIL, que era el valor que se le había asignado.

1.5  Intentar liberar la memoria dos veces en presencia de un alias.

var l1, l2 : ^Integer;
begin
	new(l1);
	l1^ := 1;
	l2 := l1; 
	...
	dispose(l1);
	dispose(l2) { ! }
end;
  • Este problema surge cuando se intenta liberar un espacio de memoria que ya fue liberado. El programa da un error en tiempo de ejecución. Es muy común caer en estas situaciones cuando se manejan alias de punteros.

2.  Problemas al utilizar listas

En estos casos se considerará la siguente declaración de tipos:

type
	ListaInt = ^Nodo;
	Nodo = record
		dato : Integer;
		sig : ListaInt;
	end;

2.1  No tener en cuenta el caso de la lista vacía o desreferenciar NIL

Procedure insertarFinal(dato : Integer; var lista : ListaInt);
var it, nuevo : ListaInt;
begin
	new(nuevo);
	nuevo^.dato := dato;
	nuevo^.sig := NIL;
	{ Falta un if que verifique que lista no sea NIL }
	it := lista;
	while (it^.sig <> NIL) do
		it := it^.sig;
	it^.sig := nuevo
end;
  • ERROR: No considera que 'lista' puede ser NIL. Normalmente no considerar este caso genera que se intente desreferenciar NIL. En este ejemplo, si la lista es NIL entonces en la primera comprobación del while se desreferencia NIL, lo que genera que el programa aborte.

2.2  No asignar NIL al final de la lista

Procedure insertarFinal(dato : Integer; var lista : ListaInt);
var it, nuevo : ListaInt;
begin
	new(nuevo);
	nuevo^.dato := dato;
	if (lista = NIL) then
		lista := nuevo
	else begin
		it := lista;
		while (it^.sig <> NIL) do
			it := it^.sig;
		it^.sig := nuevo
	end;
	{ nuevo^.sig := NIL }
end;
  • ERROR: Nunca se le asigna NIL al siguiente de 'nuevo' por lo ya no es posible saber cuando termina la lista.

2.3  Enganchar mal la lista

Procedure insertarFinal(dato : Integer; var lista : ListaInt);
var it, nuevo : ListaInt;
begin
	new(nuevo);
	nuevo^.dato := dato;
	nuevo^.sig := NIL;
	if (lista = NIL) then
		lista := nuevo
	else begin
		it := lista;
		while (it <> NIL) do { ! }
			it := it^.sig;
		it := nuevo { ! }
	end
end;
  • ERROR: En este caso se finaliza la iteración cuando el iterador es NIL y luego al iterador se lo deja apuntando al nuevo nodo. Esto solo modifica el iterador y no la lista.

2.4  Perder el inicio de la lista

Procedure insertarFinal(dato : Integer; var lista : ListaInt);
var nuevo : ListaInt;
begin
	new(nuevo);
	nuevo^.dato := dato;
	nuevo^.sig := NIL;
	if (lista = NIL) then
		lista := nuevo
	else begin
		while (lista^.sig <> NIL) do
			lista := lista^.sig; { ! }
		lista^.sig := nuevo
	end
end;
  • ERROR: Al recorrer la lista utilizando la variable 'lista' se pierde el primer elemento. Esto dejaría varios nodos de la lista inaccesibles si no hay otro puntero que apunte al inicio de la lista.
  • PREGUNTAS: Si la variable 'lista' fuese pasada por valor en lugar de por referencia: ¿Se modifica o no la lista? ¿Que pasa cuando la lista es vacía?

2.5  No liberar la memoria de una celda a eliminar

procedure eliminarPrimero(var lista : ListaInt);
{ var aBorrar : ListaInt; }
begin
	{ aBorrar := lista; }
	lista := lista^.sig;
	{ dispose(aBorrar) }
end;
  • ERROR: Este procedimiento simplemente asigna como inicio de la lista al segundo elemento, pero no libera la memoria reservada para el primer nodo.
  • PREGUNTAS: ¿Qué otro(s) error(es) tiene este procedimiento?
  • A TENER EN CUENTA: El liberar la memoria de un nodo es algo que debe hacerse con cuidado porque se debe tener seguridad de que no existen otros punteros que referencien al nodo destruido. Normalmente los procedimientos que liberan memoria indican explícitamente este comportamiento para que pueda ser tenido en cuenta.

2.6  Creer que un solo new es suficiente para todos los elementos de la lista

function copiarLista(lista : ListaInt) : ListaInt;
var listaCopia, nodoCopia, itLista : ListaInt;
begin
	if (lista = NIL) then
		listaCopia := NIL
	else begin
		new(listaCopia);
		listaCopia^.dato := lista^.dato;
		nodoCopia := listaCopia;
		itLista := lista^.sig;
		while (itLista <> NIL) do begin
			{ New(nodoCopia^.sig); }
			nodoCopia := nodoCopia^.sig;
			nodoCopia^.dato := itLista^.dato;
			itLista := itLista^.sig
		end;
		nodoCopia^.sig := NIL;
                copiarLista := listaCopia
	end
end;
  • ERROR: El error consiste en solo reservar memoria para el primer elemento cuando se debe reservar memoria para cada elemento de la lista a copiar.
  • A TENER EN CUENTA: Otros errores asociados a este tipo de ejercicios donde se debe recorrer más de una lista, es el orden de las sentencias que avanzan en las listas. Observar que si se altera el orden de las sentencias del while el programa no se comporta como se quiere.

2.7  No compartir memoria cuando se pide explícitamente que lo haga (o viceversa)

Suponiendo que la función debe devolver una copia limpia de la lista parámetro a partir del segundo elemento, o NIL si la lista es vacía:

function copiaResto(lista : ListaInt) : ListaInt;
begin
	if (lista = NIL) then
		copiaResto := NIL
	else
		copiaResto := lista^.sig; { ! }
		{ copiaResto := copiarLista(lista^.sig) } { Esta sería la sentencia correcta }
end;
  • ERROR: Esta implementación simplemente devuelve el resto de la lista pasada por parámetro, por lo que comparte memoria.

2.8  Desreferenciar incorrectamente un puntero

var lista : ListaInt;
begin
	new(lista);
	lista.dato := 5; { ERROR: falta el operador ^ para desreferenciar el puntero }
	lista^sig := NIL; { ERROR: falta el operador . para acceder al campo del registro }
	lista.Nodo.dig := 5; { ERROR: no se debe pasar por el tipo del nodo }
	lista^ := lista^.sig; { ERROR: asigna un puntero a un registro }
	lista := ^lista.sig; { ERROR: el operador ^ no está en el lugar correcto }
	lista := lista.sig^ { ERROR: el operador ^ no está en el lugar correcto }
end.

2.9  Generar una celda con basura al final de la lista

Suponiendo que se está realizando una recorrida dentro de la cual se van generando celdas para una nueva lista, un error común es ir generando anticipadamente la próxima celda. Esto ocasiona que, tras haber generado la última celda de la nueva lista, quede al final una celda adicional con basura, que nunca se llega a cargar con un valor válido.

var lista, aux : ListaInt;
begin
        { uso lista para ir recorriendo y aux para ir generando la nueva lista }
while (lista <> NIL) do begin ... aux^.dato := p^.dato; { copio el dato actual en la celda creada en la entrada previa al while } new (aux.sig); { ERROR: creo anticipadamente la siguiente celda } end;
{ cuando salí del while, quedó una celda con basura al final de la lista }
end.
Última modificación: martes, 20 de noviembre de 2018, 19:05