Вы находитесь на странице: 1из 5

..

.. Página Web:
.. http://atc1.aut.alcala.es/~infind

.. e-mail: david.jurado@uah.es

.. Despacho: E-232
Departamento de Automática

Práctica 4

Listas doblemente
enlazadas
. . . . . . . . .

Laboraratorio de Informática
Industrial II
Laboratorio de Informática Industrial II Departamento de Automática.
..
..
..
..
..
Listas de datos

Introducción al uso de listas


La lista es una estructura contenedora de datos muy usada en informática.
Dispone los datos en posiciones de memoria no necesariamente contiguas y los
enlaza usando punteros. Así se forma una lista de nodos, cada uno de los cuales
contiene al menos una referencia al siguiente (caso de listas de enlace simple), y a
menudo una referencia al anterior (caso de listas de enlace doble).

Este tipo de disposición permite con pocas operaciones de movimiento de


punteros insertar y extraer nodos sueltos o ristras de nodos.

Para obtener más información, consultar los apuntes de teoría.

Desarrollo de la práctica
En esta práctica se entrega un único archivo de código en lenguaje C que contiene
una implementación de listas. También se acompaña de algunas funciones
encargadas de informar sobre errores.

Téngase en cuenta la estructura de las funciones en las que existe una primera
parte encargada de la comprobación de errores, que aborta la ejecución de las
mismas si no se pueden satisfacer sus objetivos.

Objetivo
Se pretende optimizar el programa para poder utilizarlo desde un
microcontrolador. Además se desea hacerlo modular para poder reutilizar el
código de información de errores. Para esto será necesario analizar detenidamente
el código.

A Hacer

Se pretende utilizar el código del programa excepto el de la función main(…) y la


función HResultToString(…) para ser utilizado en un microcontrolador.

Para optimizar se ha optado por que se cree sólo una lista global de tipo
list_static_nodes en lugar de listas locales. Las funciones que la reciben
ahora como argumento, deben ser reconvertidas para que tomen implícitamente el
ejemplar global.

Se ha determinado que por necesidades de estructuración, el código definitivo


debe quedar en varios archivos con los siguientes contenidos:

• Error.h.- Constantes y prototipos relacionados con la devolución de


códigos de error.

• Error.cpp.- Definiciones de las funciones de error.

2
Filtros I: Convolución y FIR David Jurado González

• NodeData.h.- Declaración del tipo node_data, y prototipo de


ShowNodeData(…).

• Sched.h.- Prototipo para ShowListContents(…).

• Sched.cpp.- Función main(…) y ShowListContents(…). El código


de este archivo no está destinado a la compilación para el
microcontrolador sino únicamente para la comprobación del programa
desde el PC.

• List.h.- Constantes, y prototipos relacionados con la lista.

• List.cpp.- Funciones y variables relacionadas con la lista.

Ocultar las variables globales entre módulos usando static.

Crear un proyecto de tipo consola que incorpore todos estos archivos y generar el
ejecutable. El programa resultante debe tener la misma apariencia que el original.

Si fuera necesario, utilizar el depurador para comprobar el buen funcionamiento


del programa.

Cuestiones
• ¿Qué hace el programa?

• ¿Para qué crees que podría servir?

• En la programación en C se suele escribir un código similar al siguiente en los


archivos de cabecera:
List.h:

. . .
#ifndef __LIST_H__
#define __LIST_H__

. . .

#endif // __LIST_H__

¿Por qué se hace esto? Añadir un código similar a todos los archivos de cabecera
de acuerdo a sus respectivos nombres.

3
..
..
..
..
/** \file ListaEnlazada.cpp
* \author David Jurado González.
.. NS_RUNNING,
NS_WAITING,
NS_STOPPED,
///<
///<
///<
En ejecución.
En espera.
Detenido.
* \brief Ejemplo del uso de listas doblemente enlazadas. NS_LAST ///< Último valor usado en la enumeración.
* Código para facilitar el aprendizaje de Sistemas Operativos de Tiempo Real. };
* Se crea el entorno necesario para poder implementar un núcleo que gestione
* tareas en listas con 5 prioridades posibles, para ser ejecutada cada una con /// Prioridad del hilo.
* el algoritmo de Round Robin. enum node_priority
*/ {
#include <stdio.h> NP_NONE = 0, ///< Sin prioridad asignada.
#include <memory.h> NP_IDLE, ///< Muy baja.
NP_LOW, ///< Baja.
/// Tipo de datos para valor de retorno de funcinones. NP_NORMAL, ///< Normal.
#define HRESULT short NP_HIGH, ///< Alta.
NP_RT, ///< Tiempo Real.
/** \brief Macro para determinar si una función ha fallado. NP_LAST ///< Último valor usado en la enumeración.
En \a hr el código de retorno de la función. */ };
#define FAILED(hr) (hr & 0x8000)
typedef unsigned char priority_t, ///< Tipo para almacenar la prioridad de un hilo.
/** \brief Macro para determinar si una función se ha ejecutado correctamente. state_t; ///< Tipo para almacenar el estado de un hilo.
En \a hr el código de retorno de la función. */
#define SUCCEEDED(hr) (!FAILED(hr)) /** \brief Estructura con datos relativos al estado del proceso.
*/
/** \brief Macro para generar nuevos códigos de error. typedef struct node_data_
En \a typ el tipo de error reportado (cero para errores de sistema). {
En \a cod el código numérico correspondiente a ese error. */ int num;
#define MAKE_ERROR_CODE(typ, cod) ((HRESULT)(0x8000 | ((typ & 0x7f) << 8) | (cod & } node_data;
0xff)))
/** \brief Elemento de la lista de procesos.
/** \brief Macro para generar nuevos códigos de éxito en la ejecuación de la función. */
En \a typ el tipo de éxito reportado (cero para códigos de sistema). typedef struct node_
En \a cod el código numérico correspondiente a ese error. */ {
#define MAKE_SUCCESS_CODE(typ, cod) ((HRESULT)(((typ & 0x7f) << 8) | (cod & 0xff))) node_data data; ///< Datos para almacenar el estado del proceso.
state_t state; ///< Estado de activación del hilo.
/// La función se ha ejecutado correctamente. priority_t priority; ///< Prioridad del hilo.
#define S_OK 0 struct node_ *pPrev, *pNext;/**< Punteros anterior y posterior a hilos
de igual prioridad */
/// OK, el resultado es verdadero. } node;
#define S_TRUE MAKE_SUCCESS_CODE(0,1)

/// OK, el resultado es falso. /** \brief Almacén de nodos para todas las listas.
#define S_FALSE MAKE_SUCCESS_CODE(0,2) * Contenedor de todos los nodos a usar, y punteros a sus cabeceras.

. . . . . . . . .
*/
/// OK, la función se ejecutará con retraso. typedef struct list_static_nodes_
#define S_DELAYED MAKE_SUCCESS_CODE(0,3) {
short numFree; ///< Número de nodos libres.
node aNodes[NUM_NODES]; ///< Array de nodos. Para no tener que usar memoria
/// Error genérico. dinámica.
#define E_FAIL -1 node* aHeads[NP_LAST]; ///< Punteros a las cabezas de las listas.
} list_static_nodes;
/// Función no implementada.
#define E_NOTIMPL MAKE_ERROR_CODE(0,1) /** \brief Muestra información sobre los datos vinculados al nodo.
* \param pData [in] Puntero a los datos a mostrar.
/// Sin memoria para efectuar la operación. * \return Valor \c HRETURN de retorno. Puede ser uno de los siguientes valores:
#define E_OUTOFMEM MAKE_ERROR_CODE(0,2) * - \c S_OK.- La ejecución ha sido correcta.
* - \c E_INVALIDARG.- El puntero de entrada contiene un valor no válido.
/// Argumento inválido. */
#define E_INVALIDARG MAKE_ERROR_CODE(0,3) HRESULT ShowNodeData(const node_data* pData)
{
/// Se ha sobrepasado una temporización. if(pData == NULL)
#define E_OUTOFTIME MAKE_ERROR_CODE(0,4) return E_INVALIDARG;
/// Error indeterminado. printf("%d", pData->num);
#define E_UNKNOWN MAKE_ERROR_CODE(0,5)
return S_OK;
/// Se ha especificado un valor fuera de rango. }
#define E_OUTOFRANGE MAKE_ERROR_CODE(0,6)
/** Muestra el contenido de todas las listas a las que referencia \a pList.
/// El contenedor está vacío. * \param pList [in] Puntero al array de listas.
#define E_EMPTY MAKE_ERROR_CODE(0,7) * \return Valor \c HRETURN de retorno. Puede ser uno de los siguientes valores:
* - \c S_OK.- La ejecución ha sido correcta.
/// El contenedor está lleno. * - \c E_INVALIDARG.- El puntero de entrada contiene un valor no válido.
#define E_FULL MAKE_ERROR_CODE(0,8) */
HRESULT ShowListContents(const list_static_nodes* pList)
#ifndef NULL {
/// Puntero nulo. if(pList == NULL)
#define NULL ((void*) 0) return E_INVALIDARG;
#endif
printf("Número de potenciales nodos: %d\n", pList->numFree);
/** \brief Obtiene la cadena de caracteres correspondiente a
* un código resultado HRESULT. for(char cont=NP_LAST-1; cont>0; --cont)
* \note Esta función sólo se debería usar en depuración de un programa empotrado. {
* \param hr Código de error a transformar. node* pNode, *pHead;
* \return Cadena de texto explicativa del error.
*/ pHead = pList->aHeads[cont];
const char* HResultToString(HRESULT hr)
{ if(pHead == NULL)
const char* szRes; {
printf("Sin nodos para la lista %d.\n", cont);
switch(hr) continue;
{ }
case S_OK: szRes = "S_OK. "; break;
case S_TRUE: szRes = "S_TRUE: OK, el resultado es verdadero. "; break; printf("Nodos de la lista %d: \n\tContenidos:", cont);
case S_FALSE: szRes = "S_FALSE: OK, el resultado es falso. "; break;
case S_DELAYED: szRes = "S_DELAYED: OK, la función se ejecutará con retraso. pNode = pHead;
"; break;
case E_FAIL: szRes = "E_FAIL: Error genérico. "; break; do
case E_NOTIMPL: szRes = "E_NOTIMPL: Función no implementada. "; break; {
case E_OUTOFMEM: szRes = "E_OUTOFMEM: Sin memoria para efectuar la printf("[");
operación. "; break; ShowNodeData(&pNode->data);
case E_INVALIDARG: szRes = "E_INVALIDARG: Argumento inválido. "; break; printf("] ");
case E_OUTOFTIME: szRes = "E_OUTOFTIME: Se ha sobrepasado una temporización.
"; break; pNode = pNode->pNext;
case E_UNKNOWN: szRes = "E_UNKNOWN: Error indeterminado. "; break; } while(pNode != pHead);
case E_OUTOFRANGE: szRes = "E_OUTOFRANGE: Se ha especificado un valor fuera
de rango. "; break; printf("\n");
case E_EMPTY: szRes = "E_EMPTY: El contenedor está vacío. "; break; }
case E_FULL: szRes = "E_FULL: El contenedor está lleno. "; break;
default: szRes = "ERROR: Código no reconocido. "; printf("\n");
}
return S_OK;
return szRes; }
}
/** Inicia la estructura apuntada por \a pList del tipo \b list_static_nodes
/// Número máximo de hilos permitido * para poder ser utilizada por otras funciones.
#define NUM_NODES 20 * \param pList [in, out] Puntero al array de listas a ser iniciadas.
* \return Valor \c HRETURN de retorno. Puede ser uno de los siguientes valores:
/// Estado de un hilo. * - \c S_OK.- La ejecución ha sido correcta.
enum node_state * - \c E_INVALIDARG.- El puntero de entrada contiene un valor no válido.
{ */
NS_NONE = 0, ///< Sin ejecución. HRESULT InitStaticNodesList(list_static_nodes* pList)
NS_READY, ///< Preparado para ejecución. {
Laboratorio de Informática Industrial II Departamento de Automática.
..
..
node *pNode;
.. */
HRESULT AddNode(list_static_nodes* pList,
if(pList == NULL)
return E_INVALIDARG;
.. priority_t priority,
const node_data* pData,

pNode = &pList->aNodes[0];
..
memset(pList, 0, sizeof(*pList)); {
HRESULT hr;
node *pNode;
node** ppNode = NULL)

pList->aHeads[NP_NONE] = pNode;
if(ppNode)
for(int i = 1; i < NUM_NODES; i++) *ppNode = NULL;
{
node *pCurNode = &pList->aNodes[i]; if(pData == NULL || pList == NULL)
return E_INVALIDARG;
pCurNode->pPrev = pNode;
pNode->pNext = pCurNode; if(pList->numFree == NULL)
return E_OUTOFMEM;
pNode = pCurNode;
} if(priority == NP_NONE || priority >= NP_LAST)
return E_OUTOFRANGE;
pNode->pNext = &pList->aNodes[0];
pList->aHeads[NP_NONE]->pPrev = pNode; pNode = pList->aHeads[NP_NONE];

pList->numFree = NUM_NODES; hr = ExtractNode(pList, pNode);


if(FAILED(hr)) return hr;
return S_OK;
}; pNode->priority = priority;
pNode->data = *pData;
/** Extrae el nodo apuntado por \a pNode de la lista especificada por \a pList.
* \param pList [in, out] Array de listas de donde se obtiene el nodo. hr = InsertNode(pList, pNode);
* \param pNode [in, out] Puntero al nodo que se pretende extraer de la lista. if(FAILED(hr)) return hr;
* \return Valor \c HRETURN de retorno. Puede ser uno de los siguientes valores:
* - \c S_OK.- La ejecución ha sido correcta. if(ppNode)
* - \c E_OUTOFRANGE.- La prioridad del nodo no es correcta. *ppNode = pNode;
* - \c E_INVALIDARG.- Alguno de los argumentos contiene valores inválidos.
*/ return S_OK;
HRESULT ExtractNode(list_static_nodes* pList, }
node* pNode)
{ /** Libera el nodo especificado por pNode.
priority_t priority; * \param pList [in, out] Listas de nodos.
* \param pNode [in, out] Puntero al nodo que será liberado.
if(pNode == NULL || pNode->pNext == NULL || pNode->pPrev == NULL || pList == * \return Valor \c HRETURN de retorno. Puede ser uno de los siguientes valores:
NULL) * - \c S_OK.- La ejecución ha sido correcta.
return E_INVALIDARG; * - \c E_OUTOFRANGE.- La prioridad del nodo no es correcta.
* - \c E_INVALIDARG.- Alguno de los argumentos contiene valores inválidos.
priority = pNode->priority; */
HRESULT ReleaseNode(list_static_nodes* pList,
if(priority >= NP_LAST) node* pNode)
return E_OUTOFRANGE; {
HRESULT hr;
if(priority == NP_NONE) priority_t priority;
--pList->numFree;
if(pNode == NULL || pList == NULL)
pNode->pPrev->pNext = pNode->pNext; return E_INVALIDARG;
pNode->pNext->pPrev = pNode->pPrev;
priority = pNode->priority;
if(pList->aHeads[priority] == pNode)
{ if(priority = NP_NONE || priority >= NP_LAST)
if(pNode->pNext == pNode /* || pNode->pPrev == pNode */) return E_OUTOFRANGE;
pList->aHeads[priority] = NULL;
else hr = ExtractNode(pList, pNode);
pList->aHeads[priority] = pNode->pNext; if(FAILED(hr)) return hr;
}
pNode->priority = NP_NONE;
pNode->pNext = pNode->pPrev = NULL;
hr = InsertNode(pList, pNode);
return S_OK; if(FAILED(hr)) return hr;
}
return S_OK;
/** Inserta el nodo apuntado por \a pNode en la lista especificada por \a pList. }
* \param pList [in, out] Lista en donde se inserta el nodo.
* \param pNode [in, out] Puntero al nodo que se pretende insertar en la lista. /** Función de comprobación del funcionamiento del programa.
* \return Valor \c HRETURN de retorno. Puede ser uno de los siguientes valores: */
* - \c S_OK.- La ejecución ha sido correcta. int main(int argc, char *argv[])
* - \c E_OUTOFRANGE.- La prioridad del nodo no es correcta. {
* - \c E_INVALIDARG.- Alguno de los argumentos contiene valores inválidos. HRESULT hr;
*/ node_data data;
HRESULT InsertNode(list_static_nodes* pList, list_static_nodes list;
node* pNode)
{ InitStaticNodesList(&list);
node* pHead;
priority_t priority; ShowListContents(&list);
data.num = 1;
if(pNode == NULL || pNode->pNext != NULL || pNode->pPrev != NULL || pList == hr = AddNode(&list, NP_NORMAL, &data);
NULL) data.num = 2;
return E_INVALIDARG; hr = AddNode(&list, NP_NORMAL, &data);
data.num = 3;
priority = pNode->priority; hr = AddNode(&list, NP_NORMAL, &data);
data.num = 4;
if(priority >= NP_LAST) hr = AddNode(&list, NP_RT, &data);
return E_OUTOFRANGE; ShowListContents(&list);

if(priority == NP_NONE) hr = ReleaseNode(&list, list.aHeads[NP_NORMAL]->pNext);


++pList->numFree; hr = ReleaseNode(&list, list.aHeads[NP_RT]);
hr = ReleaseNode(&list, list.aHeads[NP_RT]);
pHead = pList->aHeads[priority]; ShowListContents(&list);
if(pHead == NULL)
{ for(int i = 0; i < 30; i++)
pList->aHeads[priority] = pNode; {
pNode->pNext = pNode->pPrev = pNode; node *pNode;
data.num = 3;
return S_OK; hr = AddNode(&list, NP_NORMAL, &data, &pNode);
} if(FAILED(hr))
break;
pNode->pPrev = pHead; }
pNode->pNext = pHead->pNext;
pHead->pNext = pNode; getchar();
pNode->pNext->pPrev = pNode;
return 0;
return S_OK; }
}

/** Crea un nuevo nodo y lo añade a la lista cuya prioridad sea priority
* \param pList [in, out] Listas de nodos.
* \param priority [in] Prioridad del nuevo nodo.
* \param pData [in] Datos asociados con el nuevo hilo
* \param ppNode [out, optional] Dirección de un puntero a nodo que será iniciado
* con la dirección del nuevo nodo.
* \return Valor \c HRETURN de retorno. Puede ser uno de los siguientes:
* - \c S_OK.- La ejecución ha sido correcta.
* - \c E_OUTOFRANGE.- La prioridad del nodo no es correcta.
* - \c E_INVALIDARG.- Alguno de los argumentos contiene valores inválidos.
* - \c E_OUTOFMEM.- No quedan nodos libres para añadir uno más.

Вам также может понравиться