elhacker.net cabecera Bienvenido(a), Visitante. Por favor Ingresar o Registrarse
¿Perdiste tu email de activación?.

 

 


Tema destacado: Tutorial básico de Quickjs


  Mostrar Temas
Páginas: 1 2 3 4 5 [6]
51  Programación / Programación C/C++ / ADT estructuras de datos en: 2 Julio 2010, 00:27 am
¡Buenas!

Hay que ver lo que malo es no tener nada que hacer.  ;D

Ahora que he terminado con los examenes (si ningun exito por cierto, pero admito que la culpa es solo mia), me he decidido a llevar a cabo un proyecto que me rondaba la cabeza desde hacia ya un tiempo, y como no me gusta perder el tiempo solo, aqui os lo dejo.

Se trata de codigo en C para manipular las estructuras de datos pila, cola, lista simplemente enlazada y arbol binario, planteadas de forma abstracta, de tal forma que en ellas se puede almacenar cualquier tipo de dato, sin necesidad de construir las estructuras para cada tipo de dato especifico, por lo que en principio creo que supone un ahorro con respecto al tiempo de trabajo a la hora de desarrollar alguna aplicacion que necesite utilizar estas estructuras de datos.

Aqui os dejo un lista con los tipos de datos y las operaciones que se pueden llevar a cabo sobre ellos:

PILA:
=====
+ Inicializar un nodo.
+ Saber si esta vacia.
+ Insertar un valor (push).
+ Extraer un valor (pop).

COLA:
=====
+ Inicializar un nodo.
+ Saber si esta vacia.
+ Insertar un valor (enqueue).
+ Extraer un valor (dequeue).

LISTA:
======
+ Inicializar un nodo.
+ Saber si esta vacia.
+ Insertar un valor.
+ Eliminar un valor. (y extraerlo a la vez por si hiciese falta liberar memoria)
+ Destruir la lista. (Pasa la informacion a una cola, porque esta es mas facil de liberar).
+ Buscar un valor. (retorna un puntero al valor hallado o NULL si no se encuentra)

ARBOL:
======
+ Inicializar un nodo.
+ Saber si esta vacio.
+ Insertar un valor.
+ Eliminar un valor. (y extraerlo a la vez por si hiciese falta liberar memoria)
+ Destruir el arbol. (Pasa la informacion a una cola, porque esta es mas facil de liberar).
+ Buscar un valor. (retorna un puntero al valor hallado o NULL si no se encuentra)
+ Recorrido en orden. (se almacena en una cola que se pasa como parametro)
+ Recorrido pre orden.  (se almacena en una cola que se pasa como parametro)
+ Recorrido post orden.  (se almacena en una cola que se pasa como parametro)
+ Recorrido por niveles.  (se almacena en una cola que se pasa como parametro)


Para todas las estructuras, es posible liberar la memoria dinamicamente asignada por las funciones, pero de eso se ha de encargar el que utilice el codigo, ya que el codigo esta pensado para almacenar cualquier tipo de dato, y si existiese asignacion dinamica de memoria en los datos introducidos, cualquier referencia a ella se perderia si fuese borrado el nodo que la contiene sin antes haber recuperado la informacion que contenia. Para poder liberar la memoria se procedera recuperando los datos, eliminando por medio de las funciones facilitadas de la estructura correspondiente los datos recuperados y liberando si hiciese falta cualquier asignacion dinamica de memoria en los datos recuperados.

Todas las estructuras y funciones han sido testadas y parece que funcionan correctamente. Cualquier modificacion, critica, consejo, opinion, fallo que veais... constructivos, como siempre, seran bienvenidos.

Un ultimo apunte. Algunas de las funciones tienen declarado como parametro una funcion
Código
  1. int (*cmp)(void*,void*)
Esta funcion la ha de aportar el usuario del codigo. Es un funcion para poder comparar los valores clave. He aqui algunos ejemplos:
Código
  1. int incmp(void* a,void* b)
  2. {
  3.    return *((int*)a) - *((int*)b;
  4. }
  5.  
  6. int stringcmp(void* s1, void* s2)
  7. {
  8.    return strcmp((char*)s1,(char*)s2);
  9. }
  10.  
  11. int fechacmp(void* f1,void* f2)
  12. {
  13.    if(((Fecha*)f1)->año > ((Fecha*)f2)->año)
  14.        return 1; /* la primera fecha es mayor*/
  15.    if(((Fecha*)f1)->año < ((Fecha*)f1)->año)
  16.        return -1; /* la primera fecha es menor*/
  17.  
  18.    /* ... */
  19. }
  20.  

Muchas de las funciones reciven dos punteros a void. Esto se ha echo, para poder especificar un posible struct y el campo clave que sera utilizado por la funcion cmp.

Es decir si tenemos el siguiente struct
Código
  1. struct Tonteria
  2. {
  3.    int x;
  4.    float y;
  5.    char a;
  6. };
  7.  
y el campo clave es x, se dever de poner primero el puntero al struct y luego el campo clave.

El siguiente campo, size_t size, indica a las funciones cual es el tamaño total de la estructura.

En caso de que no se utilicen estructuras, es decir, cuando se utilicen datos elementales, ambos puntero deberan ser punteros al mismo dato.

Ejemplos:
Código
  1. #include "ADTlist.h"
  2. #include "ADTlist.c" /* solo si no se va crear un proyecto */
  3.  
  4. struct Tonteria
  5. {
  6.    int x;
  7.    float y;
  8.    char a;
  9. };
  10. typedef struct Tonteria Tonteria;
  11.  
  12. int tonteriacmp(void* t1,void* t2);
  13. int intcmp(void* a,void* b);
  14.  
  15. int main(int argc, char* argv[])
  16. {
  17.    Tonteria unaTonteria;
  18.    ADTList *listaTonta=NULL, *listaInt=NULL;
  19.    int entero=5;
  20.  
  21.    Tonteria.x = 7;
  22.  
  23.    ADTListInsert(&listaTonta, &unaTonteria, &unaTonteria.x, sizeof(Tonteria), tonteriacmp);
  24.  
  25.    ADTListInsert(&listaInt, &entero, &entero, sizeof(int), intcmp);
  26.  
  27.    return 0;
  28. }
  29.  
  30. int tonteriacmp(void* t1,void* t2)
  31. {
  32.    return ((Tonteria*)t1)->x - ((Tonteria*)t2)->x;
  33. }
  34.  
  35. int intcmp(void* a,void* b)
  36. {
  37.    return *((int*)a) - *((int*)b);
  38. }
  39.  
  40.  

En el ejemplo se puede ver que todos los punteros a las estructuras que manejan las estructuras de datos han sido inicializados a NULL. No hacerlo conllevara la mayor parte de las veces un error en tiempo de ejecucion.

¡Saludos!

Para los interesados que hayan conseguido llegar hasta este punto, aqui dejo un enlace al codigo fuente. Las actualizaciones tanto en el post como en el enlace de descarga seran simultaneas.
TADEstructurasDatos.rar


TDA PILA
ADTstack.h
Código
  1. #ifndef ADT_STACK_H
  2. #define ADT_STACK_H
  3.  
  4. #include <stdlib.h>
  5. #include <string.h>
  6.  
  7. #ifndef ADT_STACK_FLAGS
  8. #define ADT_STACK_FLAGS
  9.  
  10.    #define ADT_STACK_OK                0L
  11.    #define ADT_STACK_NOT_ENOUGH_MEMORY 1L
  12.    #define ADT_STACK_EMPTY_STACK       2L
  13.  
  14. #endif /* ADT_STACK_FLAGS */
  15.  
  16. struct ADTStackNode
  17. {
  18.    void *Data; /* guarda la informacion de los datos que maneja la pila */
  19.  
  20.    struct ADTStackNode *Next; /* apunta al siguiente elemento de la pila */
  21. };
  22. typedef struct ADTStackNode ADTStackNode;
  23. typedef ADTStackNode* pADTStackNode;
  24. typedef pADTStackNode ADTStack;
  25.  
  26. /* funcion para inicializar un nodo completamente a cero*/
  27. void ADTStackNodeIni(pADTStackNode pNode);
  28.  
  29. /* Comprueba si top == NULL, equivalentemente, si la pila esta vacia */
  30. int ADTStackIsEmpty(pADTStackNode top);
  31.  
  32. /* Guarda un valor en la pila. size == sizeof el tipo de dato al que apunta data. */
  33. unsigned long ADTStackPush(pADTStackNode* top,void* data,size_t size);
  34.  
  35. /* Saca un valor de la pila y lo pone en data. size==sizeof el tipo de dato al que apunta data */
  36. unsigned long ADTStackPop(pADTStackNode* top,void* data,size_t size);
  37.  
  38. #endif /* ADT_STACK_H */
  39.  
  40.  

ADTstack.c
Código
  1. #include "ADTstack.h"
  2.  
  3. void ADTStackNodeIni(pADTStackNode pNode)
  4. {
  5.    /* inicializamos a cero la variable */
  6.    memset(pNode,0,sizeof(ADTStackNode));
  7. }
  8.  
  9. int ADTStackIsEmpty(pADTStackNode top)
  10. {
  11.    return top==NULL;
  12. }
  13.  
  14. unsigned long ADTStackPush(pADTStackNode* top,void* data,size_t size)
  15. {
  16.    pADTStackNode newNode=NULL; /* puntero al nodo que guardara la nueva informacion */
  17.  
  18.    /* intentamos asignar un nuevo nodo */
  19.    if(!(newNode = (ADTStackNode *)malloc(sizeof(ADTStackNode))))
  20.        return ADT_STACK_NOT_ENOUGH_MEMORY; /* si no se puede enviamos un aviso */
  21.  
  22.    /* inicalizamos el nuevo nodo */
  23.    ADTStackNodeIni(newNode);
  24.  
  25.    /* intentamos asignar memoria para almacenar los datos que haya que introducir en la pila */
  26.    if(!(newNode->Data = malloc(size)))
  27.    {
  28.        /* si no se puede asignar la memoria */
  29.        free(newNode); /* liberamos la memoria anteriormente asignada */
  30.        return ADT_STACK_NOT_ENOUGH_MEMORY; /* y devolvemos un aviso */
  31.    }
  32.  
  33.    /* copiamos la informacion en el nuevo nodo */
  34.    memcpy(newNode->Data , data , size);
  35.  
  36.    /* y hacemos que el nuevo nodo sea el superior de la pila */
  37.    newNode->Next = (*top); /* pasando el nodo superior a ser el segundo */
  38.  
  39.    (*top) = newNode; /* y asignando el puntero superior al nuevo nodo*/
  40.  
  41.    return ADT_STACK_OK; /* todo correcto */
  42. }
  43.  
  44. unsigned long ADTStackPop(pADTStackNode* top,void* data,size_t size)
  45. {
  46.    ADTStackNode *aux; /* para saber donde esta el segundo nodo una vez borrado el primero */
  47.  
  48.    /* no se podra sacar nada de una pila vacia */
  49.    if((*top) == NULL)
  50.        return ADT_STACK_EMPTY_STACK;
  51.  
  52.    /* apuntamos donde esta el segundo nodo */
  53.    aux = (*top)->Next;
  54.  
  55.    /* copiamos el valor que sera borrado en data */
  56.    memcpy(data , (*top)->Data , size);
  57.  
  58.    /* eliminamos la informacion contenida en el primer nodo */
  59.    free((*top)->Data);
  60.  
  61.    /* liberamos el primer nodo */
  62.    free(*top);
  63.  
  64.    /* y hacemos que el segundo nodo pase a ser el primero */
  65.    (*top) = aux;
  66.  
  67.    return ADT_STACK_OK; /* todo correcto */
  68. }
  69.  
Ejemplo:
Código
  1. #include <stdio.h>
  2.  
  3. #include "ADTstack.h"
  4.  
  5. struct Tonteria
  6. {
  7.    char c;
  8.    int x;
  9.    float y;
  10. };
  11. typedef struct Tonteria Tonteria;
  12.  
  13. void mostrarTonteria(Tonteria *t);
  14.  
  15. int main()
  16. {
  17.    ADTStackNode *pilaTonta=NULL;
  18.    Tonteria tontada;
  19.    int i;
  20.  
  21.    printf("PUSH\n");
  22.    printf("====\n");
  23.    for(i=0 ; i < 10 ; i++)
  24.    {
  25.        tontada.c = rand() % (255 - 32) + 32; /* generamos un caracter imprimible */
  26.        tontada.x = rand() % 100; /* 0 - 99 */
  27.        tontada.y = tontada.x / 10.; /* 0 - 9'9 */
  28.  
  29.        /* mostramos la informacion */
  30.        mostrarTonteria(&tontada);
  31.  
  32.        /* y la guardamos en la pila */
  33.        ADTStackPush(&pilaTonta , &tontada , sizeof(Tonteria));
  34.    }
  35.  
  36.    printf("\n");
  37.  
  38.    printf("Pulsar intro...");
  39.    getchar();
  40.    printf("\n");
  41.  
  42.    printf("POP\n");
  43.    printf("===\n");
  44.  
  45.    while(!ADTStackIsEmpty(pilaTonta))
  46.    {
  47.        /* extraemos el valor superior, que sera almacenado en tontada */
  48.        ADTStackPop(&pilaTonta , &tontada , sizeof(Tonteria));
  49.  
  50.        /* y lo mostramos para comprobar que los valores se invierten y que la pila funciona */
  51.        mostrarTonteria(&tontada);
  52.    }
  53.  
  54.    printf("\n");
  55.    printf("Pulsar intro...");
  56.    getchar();
  57.    printf("\n");
  58.  
  59.    return 0;
  60. }
  61.  
  62. void mostrarTonteria(Tonteria *t)
  63. {
  64.    printf("[%c , %02d , %3.1f]\n", t->c , t->x , t->y);
  65. }
  66.  
  67.  

TDA COLA
ADTqueue.h
Código
  1. #ifndef ADT_QUEUE_H
  2. #define ADT_QUEUE_H
  3.  
  4. #include <stdlib.h>
  5. #include <string.h>
  6.  
  7. #ifndef ADT_QUEUE_FLAGS
  8. #define ADT_QUEUE_FLAGS
  9.  
  10.    #define ADT_QUEUE_OK                0L
  11.    #define ADT_QUEUE_NOT_ENOUGH_MEMORY 1L
  12.    #define ADT_QUEUE_EMPTY_QUEUE       2L
  13.  
  14. #endif /* ADT_QUEUE_FLAGS */
  15.  
  16. struct ADTQueueNode
  17. {
  18.    void *Data;
  19.  
  20.    struct ADTQueueNode *First;
  21.    struct ADTQueueNode *Last;
  22.    struct ADTQueueNode *Next;
  23. };
  24. typedef struct ADTQueueNode ADTQueueNode;
  25. typedef ADTQueueNode ADTQueue;
  26. typedef ADTQueueNode* pADTQueueNode;
  27.  
  28. /* funcion para inicializar un nodo completamente a cero*/
  29. void ADTQueueNodeIni(pADTQueueNode pNode);
  30.  
  31. /* Comprueba si la cola esta vacia */
  32. int ADTQueueIsEmpty(pADTQueueNode queue);
  33.  
  34. /* Guarda un valor en la cabeza de la cola. size == sizeof el tipo de dato al que apunta data. */
  35. unsigned long ADTQueueEnqueue(pADTQueueNode *first,pADTQueueNode *last,void* data,size_t size);
  36.  
  37. /* Saca un valor de la cola de la cola :P y lo pone en data. */
  38. unsigned long ADTQueueDequeue(pADTQueueNode *first,pADTQueueNode *last,void* data,size_t size);
  39.  
  40. #endif /* ADT_QUEUE_H */
  41.  
  42.  

ADTqueue.c
Código
  1. #include "ADTqueue.h"
  2.  
  3. void ADTQueueNodeIni(pADTQueueNode pNode)
  4. {
  5.    /* ponemos la variable a cero */
  6.    memset(pNode,0,sizeof(ADTQueueNode));
  7. }
  8.  
  9. int ADTQueueIsEmpty(pADTQueueNode queue)
  10. {
  11.    /* comprobamos si hay datos o no en la cabeza de la cola */
  12.    return queue->First == NULL;
  13. }
  14.  
  15. unsigned long ADTQueueEnqueue(pADTQueueNode *first,pADTQueueNode *last,void* data,size_t size)
  16. {
  17.    ADTQueueNode *newNode; /* puntero a un nuevo nodo de la cola */
  18.  
  19.    /* si no podemos asignar memoria para el nuevo nodo */
  20.    if(!(newNode = (ADTQueueNode *) malloc(sizeof(ADTQueueNode))))
  21.        return ADT_QUEUE_NOT_ENOUGH_MEMORY; /* damos el aviso */
  22.  
  23.    /* inicializamos el nuevo nodo */
  24.    ADTQueueNodeIni(newNode);
  25.  
  26.    /* si no podemos asignar espacio para que el nuevo nodo contenga datos */
  27.    if(!(newNode->Data = malloc(size)))
  28.    {
  29.        free(newNode); /* eliminamos el espacio que ocupa el nodo */
  30.        return ADT_QUEUE_NOT_ENOUGH_MEMORY; /* y damos el aviso */
  31.    }
  32.  
  33.    /* copiamos la informacion en el nuevo nodo */
  34.    memcpy(newNode->Data , data , size);
  35.  
  36.    /* si la lista esta vacia */
  37.    if(!(*first))
  38.        (*first) = newNode; /* la cabeza de la cola apunta al nuevo elemento */
  39.    else /* sino */
  40.        (*last)->Next = newNode; /* añadimos el nuevo elemento al final de la cola */
  41.  
  42.    (*last) = newNode; /* y el nuevo nodo pasa a ser el ultimo de la cola */
  43.  
  44.    return ADT_QUEUE_OK;
  45. }
  46.  
  47. unsigned long ADTQueueDequeue(pADTQueueNode *first,pADTQueueNode *last,void* data,size_t size)
  48. {
  49.    ADTQueueNode* aux; /* puntero para no perder de vista el segundo elemento de la cola */
  50.  
  51.    /* si la cola esta vacia */
  52.    if(!(*first))
  53.        return ADT_QUEUE_EMPTY_QUEUE; /* avisamos de ello */
  54.  
  55.    /* guardamos la posicion del segundo elemento */
  56.    aux = (*first)->Next;
  57.  
  58.    /* recuperamos los datos del primer elemento */
  59.    memcpy(data , (*first)->Data , size);
  60.  
  61.    /* liberamos la informacion y el espacio del primer elemento */
  62.    free((*first)->Data);
  63.    free(*first);
  64.  
  65.    /* y hacemos que el segundo elemento pase a la cabeza de la cola */
  66.    (*first) = aux;
  67.  
  68.    /* si no hay mas elementos */
  69.    if(!(*first))
  70.        (*last) = NULL; /* marcamos el ultimo tambien a NUlL */
  71.  
  72.    return ADT_QUEUE_OK; /* todo correcto */
  73. }
  74.  
Ejemplo:
Código
  1. #include <stdio.h>
  2.  
  3. #include "ADTqueue.h"
  4.  
  5. struct Tonteria
  6. {
  7.    char c;
  8.    int x;
  9.    float y;
  10. };
  11. typedef struct Tonteria Tonteria;
  12.  
  13. void mostrarTonteria(Tonteria *t);
  14.  
  15. int main()
  16. {
  17.    ADTQueueNode colaTonta;
  18.    Tonteria tontada;
  19.    int i;
  20.  
  21.    /* inicializamos de forma correcta el nodo inicial */
  22.    ADTQueueNodeIni(&colaTonta);
  23.  
  24.    printf("ENQUEUE\n");
  25.    printf("=======\n");
  26.  
  27.    for(i=0 ; i < 10 ; i++)
  28.    {
  29.        tontada.c = rand() % (255 - 32) + 32; /* generamos un caracter imprimible */
  30.        tontada.x = rand() % 100; /* 0 - 99 */
  31.        tontada.y = tontada.x / 10.; /* 0 - 9'9 */
  32.  
  33.        /* mostramos la informacion */
  34.        mostrarTonteria(&tontada);
  35.  
  36.        /* y la guardamos en la cola */
  37.        ADTQueueEnqueue(&colaTonta.First , &colaTonta.Last, &tontada , sizeof(Tonteria));
  38.    }
  39.  
  40.    printf("\n");
  41.  
  42.    printf("Pulsar intro...");
  43.    getchar();
  44.    printf("\n");
  45.  
  46.    printf("\n");
  47.  
  48.    printf("DEQUEUE\n");
  49.    printf("=======\n");
  50.  
  51.    /* mientras queden elementos en la cola */
  52.    while(!ADTQueueIsEmpty(&colaTonta))
  53.    {
  54.        /* extraemos el valor superior, que sera almacenado en tontada */
  55.        ADTQueueDequeue(&colaTonta.First , &colaTonta.Last, &tontada , sizeof(Tonteria));
  56.  
  57.        /* y lo mostramos para comprobar que los valores salen en orden y que la cola funciona */
  58.        mostrarTonteria(&tontada);
  59.    }
  60.  
  61.    printf("\n");
  62.    printf("Pulsar intro...");
  63.    getchar();
  64.    printf("\n");
  65.  
  66.    return 0;
  67. }
  68.  
  69. void mostrarTonteria(Tonteria *t)
  70. {
  71.    printf("[%c , %02d , %3.1f]\n", t->c , t->x , t->y);
  72. }
  73.  

TDA LISTA
ADTlist.h
Código
  1. #ifndef ADT_LIST_H
  2. #define ADT_LIST_H
  3.  
  4. #include <stdlib.h>
  5. #include <string.h>
  6.  
  7. #include "ADTqueue.h"
  8.  
  9. #ifndef ADT_LIST_FLAGS
  10. #define ADT_LIST_FLAGS
  11.  
  12.    #define ADT_LIST_OK                0L
  13.    #define ADT_LIST_NOT_ENOUGH_MEMORY 1L
  14.    #define ADT_LIST_EMPTY_LIST        2L
  15.    #define ADT_LIST_ITEM_NOT_FOUND    4L
  16.  
  17. #endif /* ADT_LIST_FLAGS */
  18.  
  19. struct ADTListNode
  20. {
  21.    void *Data;
  22.  
  23.    struct ADTListNode *Next;
  24. };
  25. typedef struct ADTListNode ADTListNode;
  26. typedef struct ADTListNode ADTList;
  27. typedef struct ADTListNode* pADTListNode;
  28.  
  29. /* inicializamos la estructura para futuros usos */
  30. void ADTListNodeIni(pADTListNode pNode);
  31.  
  32. /* comprueba si la lista esta vacia */
  33. int ADTListIsEmpty(pADTListNode List);
  34.  
  35. /* inserta un nuevo item en la lista */
  36. unsigned long
  37. ADTListInsert(pADTListNode *List, void *data, void *key, size_t size, int (*cmp)(void*,void*));
  38.  
  39. /* elimina un item de la lista */
  40. unsigned long
  41. ADTListDelete(pADTListNode *List, void *data, void *key, size_t size, int (*cmp)(void*,void*));
  42.  
  43. /* busqueda de items en la lista */
  44. void* ADTListSearch(pADTListNode *List, void *data, void *key, int (*cmp)(void*,void*));
  45.  
  46. /* libera la memoria ocupada por la lista y la pasa a una cola, que es mas facil de liberar */
  47. unsigned long ADTListDestroy(pADTListNode *List, size_t dataSize , ADTQueueNode *pQueue);
  48.  
  49. /* retorna el primer elemento de la lista (facilita la liberacion de la memoria) */
  50. unsigned long
  51. ADTListFirst(pADTListNode *List, void *data, size_t size);
  52.  
  53. #endif /* ADT_LIST_H */
  54.  

ADTlist.c
Código
  1. #include "ADTlist.h"
  2.  
  3. void ADTListNodeIni(pADTListNode pNode)
  4. {
  5.    /* inicializamos los datos a cero */
  6.    memset(pNode,0,sizeof(ADTListNode));
  7. }
  8.  
  9. int ADTListIsEmpty(pADTListNode List)
  10. {
  11.    /* ¬¬ */
  12.    return List==NULL;
  13. }
  14.  
  15. unsigned long
  16. ADTListInsert(pADTListNode *List, void *data, void *key, size_t size, int (*cmp)(void*,void*))
  17. {
  18.    /* punteros para no perdes la pista al nodo anterior y siguiente de la busqueda de la posicion
  19.     del nuevo elemento, y el ultimo puntero sirve para asignar la memoria del nuevo dato */
  20.    ADTListNode *previous,*current,*newNode;
  21.  
  22.    /* intentamos asignar memoria para el nuevo nodo */
  23.    if(!(newNode = (ADTListNode*) malloc(sizeof(ADTListNode))))
  24.        return ADT_LIST_NOT_ENOUGH_MEMORY; /* si no se puede damos el aviso */
  25.  
  26.    /* inicializamos el nuevo nodo */
  27.    ADTListNodeIni(newNode);
  28.  
  29.    /* si no se puede asignar memoria para el nuevo dato */
  30.    if(!(newNode->Data = malloc(size)))
  31.    {
  32.        free(newNode); /* liberamos la memoria del nodo */
  33.        return ADT_LIST_NOT_ENOUGH_MEMORY; /* y avisamos de lo sucedido */
  34.    }
  35.  
  36.    /* copiamos al nuevo nodo los datos que se quieren guardar */
  37.    memcpy(newNode->Data , data , size);
  38.  
  39.    /* inicalizamos los punteros auxiliares */
  40.    previous = NULL;
  41.    current = (*List);
  42.  
  43.    /* mientras no se haya alcalzado el final de la lista y la comparacion del nuevo elemento con
  44.     el actual sea mayor (se insertaran por orden creciente de valores de comparacion) */
  45.    while(current && cmp(key , (char*)(current->Data) + ((char*)key - (char*)data))>0)
  46.    {
  47.        previous = current; /* el puntero actual pasa a ser el anterior */
  48.        current = current->Next; /* el puntero siguiente al actual pasa a ser el actual */
  49.    }
  50.  
  51.    /* si no hay elemento anterior, no se ha realizado ninguna iteracio, por lo que el nuevo
  52.     elemento debe ser el primero de la lista*/
  53.    if(!previous)
  54.    {
  55.        newNode->Next = (*List);
  56.        (*List) = newNode;
  57.    }
  58.    else /* si existe un nodo anterior */
  59.    {
  60.        /* el nuevo valor tiene que intercalarse entre el anterior y el actual */
  61.        previous->Next = newNode;
  62.        newNode->Next = current;
  63.    }
  64.  
  65.    return ADT_LIST_OK; /* todo correcto */
  66. }
  67.  
  68. unsigned long
  69. ADTListDelete(pADTListNode *List, void *data, void *key, size_t size, int (*cmp)(void*,void*))
  70. {
  71.    /* punteros auxiliares para controlar la posicion del nodo que debe ser borrado */
  72.    ADTListNode *previous,*current;
  73.  
  74.    /* no se puede eliminar nada de una lista vacia */
  75.    if(!(*List))
  76.        return ADT_LIST_EMPTY_LIST;
  77.  
  78.    /* iniciazliamos los punteros auxiliares */
  79.    previous = NULL;
  80.    current = (*List);
  81.  
  82.    /* mientras no se haya alcalzado el final de la lista y la comparacion del nuevo elemento con
  83.     el actual sea mayor (los elementos estan insertados por orden creciente) */
  84.    while(current && cmp(key ,(char*)(current->Data) + ((char*)key - (char*)data))>0)
  85.    {
  86.        previous = current; /* anterior = actual */
  87.        current = current->Next; /* el siguiente al actual pasa a ser el nuevo actual */
  88.    }
  89.  
  90.    /* si se ha alcanzado el final de la lista */
  91.    if(!current)
  92.        return ADT_LIST_ITEM_NOT_FOUND; /* significa que el elemento no existe */
  93.  
  94.    /* si la comparacion no es igual */
  95.    if(cmp(key ,(char*)(current->Data) + ((char*)key - (char*)data)))
  96.        return ADT_LIST_ITEM_NOT_FOUND; /* el elemento no existe */
  97.  
  98.    /* copiamos la informacion contenida antes de borrar el nodo */
  99.    memcpy(data , current->Data , size);
  100.  
  101.    /* si no hay anterior*/
  102.    if(!previous)
  103.        (*List) = (*List)->Next; /* hay que eliminar el primer nodo */
  104.    else /* sino */
  105.        previous->Next = current->Next /* el anterior salta al actual */;
  106.  
  107.    /* liberamos los datos del nodo y el propio nodo */
  108.    free(current->Data);
  109.    free(current);
  110.  
  111.    return ADT_LIST_OK; /* todo correcto */
  112. }
  113.  
  114. void* ADTListSearch(pADTListNode *List, void *data, void *key, int (*cmp)(void*,void*))
  115. {
  116.    /* puntero auxiliar para recorrer la lista */
  117.    ADTListNode *current;
  118.  
  119.    current = (*List);
  120.  
  121.    /* mientras no se haya alcalzado el final de la lista y la comparacion del nuevo elemento con
  122.     el actual sea mayor (se insertaran por orden creciente de valores de comparacion) */
  123.    while(current && cmp(key ,(char*)(current->Data) + ((char*)key - (char*)data))>0)
  124.        current = current->Next;
  125.  
  126.    /* si se ha alcanzado el final de la lista */
  127.    if(!current)
  128.        return NULL; /* no se ha encontrado el elemento y se devuelve NULL */
  129.  
  130.    /* si el elemento actual es mayor que el buscado, el elemento buscado no existe */
  131.    if(cmp(key ,(char*)(current->Data) + ((char*)key - (char*)data)))
  132.        return NULL;
  133.  
  134.    /* retornamos un puntero al valor encontrado */
  135.    return current->Data;
  136. }
  137.  
  138. unsigned long ADTListDestroy(pADTListNode *List, size_t dataSize , ADTQueueNode *pQueue)
  139. {
  140.    unsigned long ret;
  141.  
  142.    /* lista vacia o final de la lista */
  143.    if(!(*List))
  144.        return ADT_LIST_EMPTY_LIST; /* avisamos */
  145.  
  146.    /* almacenamos el valor de retorno para ver que ha sucedido al eliminar el resto de nodos */
  147.    ret = ADTListDestroy(&((*List)->Next) , dataSize , pQueue);
  148.  
  149.    /* los unicos valores que retorna la funcion de forma directa son ADT_LIST_EMMPTY_LIST, que
  150.        indica que se ha llegado al final de la lista, o ADT_LIST_OK, que indica que de momento no
  151.        ha ocurrido ningun error. Ambos valores son indicativos de que no hay errores.
  152.  
  153.        Si ADTQueueEnqueue retorna ADT_QUEUE_OK, este valor es igual a ADT_LIST_OK por definicion
  154.  
  155.        El resto de los valores que puede retornar provienen de algun posible error a la hora de
  156.        manipular la cola que almacenara los datos de la lista
  157.     */
  158.    if(ret == ADT_LIST_EMPTY_LIST || ret == ADT_LIST_OK)
  159.    {
  160.        /* en este punto no ha habido ningun error */
  161.  
  162.        /* intentamos almacenar el siguiente valor en la cola */
  163.        if(!(ret = ADTQueueEnqueue(&(pQueue->First), &(pQueue->Last), (*List)->Data, dataSize)))
  164.        {
  165.            /* se ha podido insertar el valor en la cola */
  166.  
  167.            /* liberamos la informacion contenida en el nodo actual y el propio nodo */
  168.            free((*List)->Data);
  169.            free((*List));
  170.  
  171.            return ADT_LIST_OK; /* y avisamos de que todo va bien */
  172.        }
  173.    }
  174.  
  175.    /* si se llega a este punto, ha habido algun error */
  176.    return ret; /* avisamos del error ocurrido */
  177. }
  178.  
  179. unsigned long
  180. ADTListFirst(pADTListNode *List, void *data, size_t size)
  181. {
  182.    /* no hay primer elemento en una lista vacia */
  183.    if(!(*List))
  184.        return ADT_LIST_EMPTY_LIST;
  185.  
  186.    /* copiamos la informacion del primer elemento */
  187.    memcpy(data , (*List)->Data , size);
  188.  
  189.    return ADT_LIST_OK; /* todo correcto */
  190. }
  191.  
Ejemplo:
Código
  1. #include <stdio.h>
  2.  
  3. #include "ADTlist.h"
  4.  
  5. struct Tonteria
  6. {
  7.    char c;
  8.    int x;
  9.    float y;
  10. };
  11. typedef struct Tonteria Tonteria;
  12.  
  13. int intcmp(void *a,void *b);
  14. void mostrarTonteria(Tonteria *t);
  15.  
  16. int main()
  17. {
  18.    ADTListNode *listaTonta=NULL;
  19.    Tonteria tontada , *pTonto;
  20.    int i;
  21.  
  22.    printf("INSERCION\n");
  23.    printf("=========\n");
  24.  
  25.    for(i=0 ; i < 10 ; i++)
  26.    {
  27.        tontada.c = rand() % (255 - 32) + 32; /* generamos un caracter imprimible */
  28.        tontada.x = rand() % 100; /* 0 - 99 */
  29.        tontada.y = tontada.x / 10.; /* 0 - 9'9 */
  30.  
  31.  
  32.        /* mostramos la informacion */
  33.        mostrarTonteria(&tontada);
  34.  
  35.        /* y la guardamos en la lista ordenando los elementos por el valor x */
  36.        ADTListInsert(&listaTonta , &tontada , &tontada.x , sizeof(Tonteria) , intcmp);
  37.    }
  38.  
  39.    printf("\n");
  40.  
  41.    getchar();
  42.  
  43.    printf("BUSQUEDA\n");
  44.    printf("========\n");
  45.  
  46.    /* aunque no se retorne ningun valor en tontada, es necesario pasar el puntero para que la
  47.     funcion puede calcular el desplazamiento que tiene el campo clave dentro del struct */
  48.    if((pTonto = (Tonteria*) ADTListSearch(&listaTonta , &tontada , &tontada.x , intcmp)))
  49.    {
  50.        printf("%d se encuentra en la lista.\n",pTonto->x);
  51.    }
  52.    else
  53.    {
  54.        printf("%d no se encuentra en la lista.\n",tontada.x);
  55.    }
  56.  
  57.    tontada.x = 5;
  58.  
  59.    if((pTonto = (Tonteria*) ADTListSearch(&listaTonta , &tontada , &tontada.x , intcmp)))
  60.    {
  61.        printf("%d se encuentra en la lista.\n",pTonto->x);
  62.    }
  63.    else
  64.    {
  65.        printf("%d no se encuentra en la lista.\n",tontada.x);
  66.    }
  67.  
  68.    tontada.x = 27;
  69.  
  70.    if((pTonto = (Tonteria*) ADTListSearch(&listaTonta , &tontada , &tontada.x , intcmp)))
  71.    {
  72.        printf("%d se encuentra en la lista.\n",pTonto->x);
  73.    }
  74.    else
  75.    {
  76.        printf("%d no se encuentra en la lista.\n",tontada.x);
  77.    }
  78.  
  79.    printf("\n");
  80.  
  81.    getchar();
  82.  
  83.    printf("ELIMINAR DATO\n");
  84.    printf("=============\n");
  85.  
  86.    tontada.x = 27;
  87.  
  88.    ADTListDelete(&listaTonta , &tontada , &tontada.x , sizeof(Tonteria) , intcmp);
  89.  
  90.    if((pTonto = (Tonteria*) ADTListSearch(&listaTonta , &tontada , &tontada.x , intcmp)))
  91.    {
  92.        printf("%d se encuentra en la lista.\n",pTonto->x);
  93.    }
  94.    else
  95.    {
  96.        printf("%d no se encuentra en la lista.\n",tontada.x);
  97.    }
  98.  
  99.    printf("\n");
  100.  
  101.    getchar();
  102.  
  103.    printf("VACIADO\n");
  104.    printf("=======\n");
  105.  
  106.    while(!ADTListIsEmpty(listaTonta))
  107.    {
  108.        ADTListFirst(&listaTonta , &tontada , sizeof(Tonteria));
  109.  
  110.        ADTListDelete(&listaTonta , &tontada , &tontada.x ,sizeof(Tonteria) , intcmp);
  111.  
  112.        printf("Eliminado: ");
  113.  
  114.        mostrarTonteria(&tontada);
  115.    }
  116.  
  117.    getchar();
  118.  
  119.    return 0;
  120. }
  121.  
  122. int intcmp(void *a,void *b)
  123. {
  124.    return *((int*)a) - *((int*)b);
  125. }
  126.  
  127. void mostrarTonteria(Tonteria *t)
  128. {
  129.    printf("[%c , %02d , %3.1f]\n", t->c , t->x , t->y);
  130. }
  131.  
  132.  

TDA ARBOL BINARIO
ADTbintree.h
Código
  1. #ifndef ADT_BINTREE_H
  2. #define ADT_BINTREE_H
  3.  
  4. #include <stdlib.h>
  5. #include <string.h>
  6.  
  7. #include "ADTqueue.h"
  8.  
  9. #ifndef ADT_BINTREE_FLAGS
  10. #define ADT_BINTREE_FLAGS
  11.  
  12.    #define ADT_BINTREE_OK                0L
  13.    #define ADT_BINTREE_NOT_ENOUGH_MEMORY 1L
  14.    #define ADT_BINTREE_EMPTY_BINTREE     2L
  15.    #define ADT_BINTREE_ITEM_NOT_FOUND    4L
  16.    #define ADT_BINTREE_DUPLICATED_ITEM   8L
  17.  
  18. #endif /* ADT_BINTREE_FLAGS */
  19.  
  20. struct ADTBinTreeNode
  21. {
  22.    void *Data;
  23.  
  24.    struct ADTBinTreeNode* Left;
  25.    struct ADTBinTreeNode* Right;
  26. };
  27. typedef struct ADTBinTreeNode ADTBinTreeNode;
  28. typedef ADTBinTreeNode ADTBinTree;
  29. typedef ADTBinTreeNode* pADTBinTreeNode;
  30.  
  31. /* inicializa a cero la estructura para que funcine correctamente cuando sea utilizada */
  32. void ADTBinTreeNodeIni(pADTBinTreeNode pNode);
  33.  
  34. /* determina si el arbol esta vacio */
  35. int ADTBinTreeIsEmpty(pADTBinTreeNode Root);
  36.  
  37. /* insterta un nuevo dato en el arbol, eliminando duplicados de forma automatica */
  38. unsigned long
  39. ADTBinTreeInsert(pADTBinTreeNode *Root, void *data, void *key, size_t size, int (*cmp)(void*,void*));
  40.  
  41. /* elimina un dato del arbol */
  42. unsigned long
  43. ADTBinTreeDelete(pADTBinTreeNode *Root, void *data, void *key, size_t size, int (*cmp)(void*,void*));
  44.  
  45. /* busca a traves del campo clave key un elemento del arbol, guarda la info en data y devuelve un
  46. puntero al dato encontrado si existe, en caso contrario devuelve NULL */
  47. void* ADTBinTreeSearch(pADTBinTreeNode *Root, void *data, void *key, int (*cmp)(void*,void*));
  48.  
  49. /* libera la memoria ocupada por el arbol y la pasa a una cola, que es mas facil de liberar */
  50. unsigned long ADTBinTreeDestroy(pADTBinTreeNode *Root, size_t dataSize , ADTQueueNode *pQueue);
  51.  
  52. /* recorrido en orden del arbol. Los datos se almacenan en la cola *pQueue */
  53. unsigned long ADTBinTreeInOrder(ADTBinTreeNode* Root,size_t dataSize,ADTQueueNode* pQueue);
  54.  
  55. /* recorrido preorden del arbol. Los datos se almacenan en la cola *pQueue */
  56. unsigned long ADTBinTreePreOrder(ADTBinTreeNode* Root,size_t dataSize,ADTQueueNode* pQueue);
  57.  
  58. /* recorrido post orden del arbol. Los datos se almacenan en la cola *pQueue */
  59. unsigned long ADTBinTreePostOrder(ADTBinTreeNode* Root,size_t dataSize,ADTQueueNode* pQueue);
  60.  
  61. /* recorrido por niveles del arbol. Los datos se almacenan en la cola *pQueue */
  62. unsigned long ADTBinTreeLevelOrder(ADTBinTreeNode* Root,size_t dataSize,ADTQueueNode* pQueue);
  63.  
  64. #endif /* ADT_BINTREE_H */
  65.  

ADTbintree.c
[code=c]
#include "ADTbintree.h"

void ADTBinTreeNodeIni(pADTBinTreeNode pNode)
{
    /* ponemos a cero la estructura al completo */
    memset(pNode , 0 , sizeof(ADTBinTreeNode));
}

int ADTBinTreeIsEmpty(pADTBinTreeNode Root)
{
    /* evidente. ;D */
    return Root == NULL;
}

unsigned long
ADTBinTreeInsert(pADTBinTreeNode *Root, void *data, void *key, size_t size, int (*cmp)(void*,void*))
{
    /* si hemos llegado a un nodo vacio, se guardara en el la nueva informacion */
    if(!(*Root))
    {
        /* si no conseguimos reservar memoria para el nuevo nodo */
        if(!((*Root) = (ADTBinTreeNode*) malloc(sizeof(ADTBinTreeNode))))
            return ADT_BINTREE_NOT_ENOUGH_MEMORY; /* avisamos de lo sucedido */

        /* inicializamos el nuevo nodo para evitar errores */
        ADTBinTreeNodeIni(*Root);

        /* si no podemos asignar memoria para el nuevo dato */
        if(!((*Root)->Data = malloc(size)))
        {
            /* eliminamos la memoria asignada para el nuevo nodo */
            free(*Root);
            /* dejamos el puntero a NULL para identificarlo como libre en llamadas sucesivas */
            (*Root) = NULL;

            return ADT_BINTREE_NOT_ENOUGH_MEMORY; /* y avisamos de lo sucedido */
        }

        /* llegados a este punto se ha podido asignar toda la memoria necesaria */

        /* copiamos el nuevo valor en el nodo */
        memcpy((*Root)->Data , data , size);

        return ADT_BINTREE_OK; /* y avisamos de que se ha podido guardar el dato */
    }

    /* si el campo clave da una comparacion menor */
    if(cmp(key , (char*)((*Root)->Data) + ((char*)key - (char*)data)) < 0)
    {
        /* el valor se guardara en el nodo izquierdo */
        return ADTBinTreeInsert(&((*Root)->Left) , data , key , size , cmp);
    }
    else if(cmp(key , (char*)((*Root)->Data) + ((char*)key - (char*)data)) > 0)
    {
        /* si la comparacion da mayor, entonces se guardara en el derecho */
        return ADTBinTreeInsert(&((*Root)->Right) , data , key , size , cmp);
    }

    /* si se llega aqui significa que la comparacion da cero como resultado, por lo que el nodo
    ya esixte en el arbol */
    return ADT_BINTREE_DUPLICATED_ITEM; /* avisamos de que el valor ya existia */
}

unsigned long
ADTBinTreeDelete(pADTBinTreeNode *Root, void *data, void *key, size_t size, int (*cmp)(void*,void*))
{
    /* punteros auxiliars para recorrer el arbol, apuntar al nodo de reemplazo y al nodo borrado*/
    pADTBinTreeNode *reader,replacement,aux;

    /* Si la raiz esta vacia, o el arbol esta vacio o no se ha encontrado el elemento */
    if(!(*Root))
        return ADT_BINTREE_ITEM_NOT_FOUND; /* siempre avisaremos de que no existe el elemento */

    /* si encontramos una coincidencia*/
    if(cmp(key , (char*)((*Root)->Data) + ((char*)key - (char*)data)) == 0)
    {
        /* recuperamos la informacion del nodo para liberar, si procede, memoria dinamica */
        memcpy(data , (*Root)->Data , size);

        /* *Root es el puntero del nodo padre que apunta al nodo que hay que eliminar */
        aux = (*Root); /* recordamos donde esta el nodo que hay que eliminar */

        /* si el nodo que hay que eliminar es un nodo sin hijos */
        if(!aux->Left && !aux->Right)
        {
            /* liberamos la informacion contenida en el nodo y tambien el nodo */
            free(aux->Data);
            free(aux);

            /* e identificamos el puntero del nodo padre como un puntero libre */
            (*Root) = NULL;

            return ADT_BINTREE_OK; /* avisamos de que todo ha ido bien */
        }

        /*
         * Si el nodo que ha de ser borrado es un nodo con un solo hijo, dicho hijo sera el nodo
         * de reemplazo, por lo que pasara a acupar el lugar del nodo borrado.
         */
        if(!aux->Left && aux->Right)
        {
            (*Root) = aux->Right;

            free(aux->Data);
            free(aux);

            return ADT_BINTREE_OK;
        }

        if(aux->Left && !aux->Right)
        {
            (*Root) = aux->Left;

            free(aux->Data);
            free(aux);

            return ADT_BINTREE_OK;
        }

        /* llegados a este punto, sabemos que el nodo que ha de ser borrado tiene dos hijos */

        /* utilizaremos como nodo de reemplazo el mayor de los nodos menores */

        reader = &((*Root)->Left);

        /* buscar el mayor de los nodos menores*/
        while((*reader)->Right)
            reader = &((*reader)->Right);

        /* apuntamos al nodo de reemplazo*/
        replacement = (*reader);

        /* Asignamos al puntero del nodo padre que apunta al nodo de reemplazo la rama izda, ya
        que cualquier nodo de esta rama es mayor que el nodo padre (recordar que hemos ido hacia
        derecha, lo que implica que siempre hemos buscado valores cada vez mayores, y por lo tento
        (*reader) apunta al nodo derecho del padre del nodo de reemplazo */
        (*reader) = (*reader)->Left;

        /* como el nodo de reemplazo es el menor de todos los nodos mayores que el que hay que
        eliminar, en particular sera mayor que todo el subarbol izquierdo del nodo a eliminar y
        como hemos buscado el mayor de los valores menores, en particular sera menor que cualquier
        nodo mayor que el que queremos eliminar. Es decir, el subarbol izquierdo del nodo de
        eliminado sera el subarbol izquierdo del nodo de reemplazo y el subarbol derecho del nodo
        eliminado el derecho del nodo de reemplazo */
        replacement->Left = (*Root)->Left;
        replacement->Right = (*Root)->Right;

        /* En este punto ya tenemos la estructura final del arbol tras eliminar el nodo */

        /* pasamos a eliminar la informacion contenida en el nodo que hay que eliminar y el mismo
        nodo */
        free(aux->Data);
        free(aux);

        /* y hacemos que el puntero del nodo padre apunte al nodo de reemplazo */
        (*Root) = replacement;

        return ADT_BINTREE_OK; /* todo correcto */
    }


    /* si el valor clave es menor que el valor del nodo*/
    if(cmp(key , (char*)((*Root)->Data) + ((char*)key - (char*)data)) < 0)
    {
        /* buscamos el nodo que hay que eliminar en el subarbol izquierdo */
        return ADTBinTreeDelete(&((*Root)->Left) , data , key , size , cmp);
    }
    else if(cmp(key , (char*)((*Root)->Data) + ((char*)key - (char*)data)) > 0)
    {
        /* sino lo buscamos en el derecho */
        return ADTBinTreeDelete(&((*Root)->Right) , data , key , size , cmp);
    }

    return ADT_BINTREE_ITEM_NOT_FOUND; /* para evitar warnings del compilador */
}

void* ADTBinTreeSearch(pADTBinTreeNode *Root, void *data, void *key, int (*cmp)(void*,void*))
{
    /* si se ha llegado a un nodo vacio */
    if(!(*Root))
        return NULL; /* no existe el elemento buscado */

    /* si el elemento buscado coincide con el del nodo */
    if(cmp(key , (char*)((*Root)->Data) + ((char*)key - (char*)data)) == 0)
        return (*Root)->Data; /* devolvemos un puntero al dato que contiene el nodo */


    /* si el campo clave es menor */
    if(cmp(key , (char*)((*Root)->Data) + ((char*)key - (char*)data)) < 0)
    {
        /* buscamos en el subarbol izquierdo */
        return ADTBinTreeSearch(&((*Root)->Left) , data , key , cmp);
    }
    else if(cmp(key , (char*)((*Root)->Data) + ((char*)key - (char*)data)) > 0)
    {
        /* si es mayor, buscamos en el derecho */
        return ADTBinTreeSearch(&((*Root)->Right) , data , key , cmp);
    }

    return NULL; /* para evitar warnings del compilador */
}

unsigned long ADTBinTreeDestroy(pADTBinTreeNode *Root, size_t dataSize , ADTQueueNode *pQueue)
{
    unsigned long ret;

    /* arbol vacio o nodo libre */
    if(!(*Root))
        return ADT_BINTREE_EMPTY_BINTREE; /* avisamos */

    /* almacenamos el valor de retorno para ver que ha sucedido al eliminar el resto de nodos */

    ret = ADTBinTreeDestroy(&((*Root)->Left) , dataSize , pQueue);

    /* los unicos valores que retorna la funcion de forma directa son
       ADT_BINTREE_EMPTY_BINTREE, que indica que se ha llegado a un nodo libre, o ADT_BINTREE_OK,
       que indica que de momento no ha ocurrido ningun error. Ambos valores son indicativos de
       que no hay errores.

       Si ADTQueueEnqueue retorna ADT_QUEUE_OK este valor es igual a ADT_BINTREE_OK por definicion

       El resto de los valores que puede retornar provienen de algun posible error a la hora de
       manipular la cola que almacenara los datos de la lista
    */

    if(ret != ADT_BINTREE_OK && ret != ADT_BINTREE_EMPTY_BINTREE)
        return ret; /* avisamos de que ha ocurrido algun error */

    /* almacenamos el valor de retorno para ver que ha sucedido al eliminar el resto de nodos */
    ret = ADTBinTreeDestroy(&((*Root)->Right) , dataSize , pQueue);

    if(ret == ADT_BINTREE_EMPTY_BINTREE || ret == ADT_BINTREE_OK)
    {
        /* en este punto no ha habido ningun error */

        /* intentamos almacenar el siguiente valor en la cola */
        if(!(ret = ADTQueueEnqueue(&(pQueue->First), &(pQueue->Last), (*Root)->Data, dataSize)))
        {
            /* se ha podido insertar el valor en la cola */

            /* Tanto el subarbol izquierdo como el derecho han sido eliminados asi que ahora
               liberamos la informacion contenida en el nodo actual y el propio nodo */
            free((*Root)->Data);
            free(*Root);

            return ADT_BINTREE_OK; /* y avisamos de que todo va bien */
        }
    }

    /* si se llega a este punto, ha habido algun error */
    return ret; /* avisamos del error ocurrido */
}

unsigned long ADTBinTreeInOrder(ADTBinTreeNode* Root,size_t dataSize,ADTQueueNode* pQueue)
{
    if(Root)
    {
        void *data;
        unsigned long ret;

        /* si no se puede asignar memoria suficiente para almacenar los datos manejados */
        if(!(data = malloc(dataSize)))
        {
            return ADT_BINTREE_NOT_ENOUGH_MEMORY; /* avisamos de lo ocurrido */
        }

        /* buscamos siempre el elemento mas pequeño */
        ADTBinTreeInOrder(Root->Left,dataSize,pQueue);

        /* lo almacenamos */
        /* si ocurre algun error al almacenar el valor en la cola */
        if((ret = ADTQueueEnqueue(&(pQueue->First),&(pQueue->Last),Root->Data,dataSize)))
        {
            /* vaciamos la cola */
            while(!ADTQueueEnqueue(&(pQueue->First),&(pQueue->Last),data,dataSize));

            /* liberamos la memoria auxiliar */
            free(data);

            return ret; /* y avisamos de lo ocurrido */
        }

        /* y seguimos obteniendo los que son mayores */
        ADTBinTreeInOrder(Root->Right,dataSize,pQueue);

        free(data);

        return ADT_BINTREE_OK;
    }

    return ADT_BINTREE_EMPTY_BINTREE;
}

unsigned long ADTBinTreePreOrder(ADTBinTreeNode* Root,size_t dataSize,ADTQueueNode* pQueue)
{
    if(Root)
    {
        void *data; /* si ocurriese algun error la utilizaremos para vaciar la pila */
        unsigned long ret; /* si hay errores almacenara el valor que hay que retornar */

        /* si no se puede asignar memoria suficiente para almacenar los datos manejados */
        if(!(data = malloc(dataSize)))
        {
            return ADT_BINTREE_NOT_ENOUGH_MEMORY; /* avisamos de lo ocurrido */
        }

        /* primero se extrae el valor del nodo actual */

        /* si ocurre algun error al almacenar el valor en la cola */
        if((ret = ADTQueueEnqueue(&(pQueue->First),&(pQueue->Last),Root->Data,dataSize)))
        {
            /* vaciamos la cola */
            while(!ADTQueueEnqueue(&(pQueue->First),&(pQueue->Last),data,dataSize));

            /* liberamos la memoria signada para almacenar los datos */
            free(data);

            /* y damos aviso de lo ocurrido */
            return ret;
        }

        /* y luego los de los hijos */
        ADTBinTreePreOrder(Root->Left,dataSize,pQueue);

        ADTBinTreePreOrder(Root->Right,dataSize,pQueue);

        free(data);

        return ADT_BINTREE_OK;
    }

    return ADT_BINTREE_EMPTY_BINTREE;
}

unsigned long ADTBinTreePostOrder(ADTBinTreeNode* Root,size_t dataSize,ADTQueueNode* pQueue)
{
    if(Root)
    {
        void *data;
        unsigned long ret;

        /* si no se puede asignar memoria suficiente para almacenar los datos manejados */
        if(!(data = malloc(dataSize)))
        {
            return ADT_BINTREE_NOT_ENOUGH_MEMORY; /* avisamos de lo ocurrido */
        }

        /* primero se extraen los hijos */
        ADTBinTreeInOrder(Root->Left,dataSize,pQueue);

        ADTBinTreeInOrder(Root->Right,dataSize,pQueue);

        /* y luego el padre */
        /* si ocurre algun error al almacenar el valor en la cola */
        if((ret = ADTQueueEnqueue(&(pQueue->First),&(pQueue->Last),Root->Data,dataSize)))
        {
            /* vaciamos la cola */
            while(!ADTQueueEnqueue(&(pQueue->First),&(pQueue->Last),data,dataSize));

            /* liberamos la memoria auxiliar */
            free(data);

            return ret; /* y avisamos de lo ocurrido */
        }

        free(data);

        return ADT_BINTREE_OK;
    }

    return ADT_BINTREE_EMPTY_BINTREE;
}

unsigned long ADTBinTreeLevelOrder(ADTBinTreeNode* Root,size_t dataSize,ADTQueueNode* pQueue)
{
    if(Root)
    {
        /*
         * variable auxiliares para:
         *
         * - Almacenar los nodos del arbol.
         *
         * - Crear una lista para almacenar los nodos del arbol y vaciar la cola auxiliar si
         *   hubiese un error
         *
         * - Reservar memoria para vaciar la cola si ocurriese un error
         *
         * - Recivir el valor de retorno tras la insercion de un dato en la cola.
         */

        ADTBinTreeNode auxTreeNode;
        ADTQueueNode auxQueue;
        void *data;
        unsigned long ret=0;

        /* si no se puede asignar memoria suficiente para almacenar los datos manejados */
        if(!(data = malloc(dataSize)))
            return ADT_BINTREE_NOT_ENOUGH_MEMORY; /* avisamos de lo ocurrido */

        /* inicializamos la cola */
        ADTQueueNodeIni(&auxQueue);

        /* introducimos el nodo raiz del arbol en la cola auxiliar */
        /* si ocurre algun error al almacenar el valor en la cola auxiliar */
        if((ret=ADTQueueEnqueue(&auxQueue.First , &auxQueue.Last , Root , sizeof(ADTBinTreeNode))))
        {
            /* liberamos la memoria */
            free(data);

            return ret; /* y avisamos de lo sucedido */
        }

        /* mientras haya valores en la cola auxiliar */
        while(!ADTQueueIsEmpty(&auxQueue))
        {
            /* sacamos el nodo actual */
            ADTQueueDequeue(&auxQueue.First,&auxQueue.Last,&auxTreeNode,sizeof(ADTBinTreeNode));

            /* intentamos guardar el dato que contiene en la cola */
            /* si ocurre algun error al almacenar el valor en la cola */
            if((ret=ADTQueueEnqueue(&(pQueue->First), &(pQueue->Last), auxTreeNode.Data, dataSize)))
            {
                /* vaciamos la cola auxiliar*/
                while(!
                    ADTQueueDequeue(&auxQueue.First,&auxQueue.Last,&auxTreeNode,
                                    sizeof(ADTBinTreeNode))
                     );

                /* vaciamos la cola */
                while(!ADTQueueDequeue(&(pQueue->First) , &(pQueue->Last) , data , dataSize));

                /* liberamos la memoria */
                free(data);

                /* y avidamos del error */
                return ret;
            }

            /* si el nodo extraido tiene un hijo de menor valor */
            if(auxTreeNode.Left)
            {
                /* lo almacenamos en la cola auxiliar */
                /* si ocurre algun error al almacenar el valor en la cola auxiliar */
                if(
                    (ret = ADTQueueEnqueue(&auxQueue.First,&auxQueue.Last,auxTreeNode.Left,
                                &nbs
52  Programación / Programación General / ¿Que tengo que hacer para que mi codigo este bajo GPL 3? en: 3 Junio 2010, 11:38 am
¡Buenas a todos!

No se si este es el subforo correspondiente a la pregunta. Si no es asi que sea trasladado donde corresponda.

Bueno, pues el asunto deja clara mi duda. Tengo una seria de codigos (headers e implementaciones de ellos) que me gustaria que estuviesen bajo licencia GPL, pero no tengo ni la mas remota idea del proceso que hay que seguir para que esten bajo esta licencia.

He ojeado algo lo que es la pagina de la FSF, pero mi ingles es bastante escueto, asi que no he sabido ni por donde empezar.

Agradeceria cualquier orientacion o explicacion.

Un saludo y gracias de ante mano.
53  Informática / Software / [solucionado] ¿eMule + preferences.ini + Windows Vista? en: 31 Mayo 2010, 02:40 am
¡Muy buenas!

Acabo de hacer un programa que tiene que manipular los valores del fichero preferences.ini del eMule. En mi ordenador, con winXP no hay ningun problema, el fichero esta dentro de la carpeta config en el directorio del eMule.

El problema esta en Windows Vista, que me estoy volviendo loco buscando el dichoso fichero y no lo encuentro por ningun lado. Incluso el buscador me dice que no hay ningun fichero con ese nombre, pero creo que por algun lado tiene que estar, ya que de una ejecucion a otra, eMule mantiene las modificaciones de configuracion que aplico.

¿Sabeis donde puede estar el fichero?

¡Gracias!

PD: Jugando con Mr. Google ya lo he encontrado. Para el que le haga falta...

C:\ProgramData\eMule\config

¡Saludos!
54  Media / Multimedia / ¿Cual es el formato de video mas simple? en: 8 Marzo 2010, 18:28 pm
¡Buenas!

Por si la pregunta genera dudas me explico. Por ejemplo, para manejar imagenes, a la hora de hacer un programa que cree un fichero de imagen y/o lo manipule, lo mas facil es el formato BMP, ya que la imagen se guarda (por regla general) a lo bestia, describiendo los pixeles en RGB, y en el encabezado del fichero, en unos pocos bytes, esta toda la informacion necesaria para describir el fichero.

En el caso del video, ¿existe algun formato parecido?, por ejemplo, un encabezado en el que en unos pocos bytes se diga el numero y dimensiones de los fotogramas, y luego venga cada uno de los fotogramas... o algo sencillo... No me hace falta utilizar audio.

¡Gracias y hasta luego!
55  Media / Diseño Gráfico / ¿Cual estructura interna de un BitMap? en: 8 Febrero 2010, 11:35 am
¡Muy buenas a todos!

Estoy trabajndo en un programa que estganografia un determinado texto en un archivo bmp, y me estoy encontrando que (al parecer) hay muy poca informacion sobre la estructura interna de los ficheros bmp. Hasta ahora he conseguido informacion util sobre este formato, siempre y cuando trate con ficheros sin compresion con 24 bits por color, y algo mas detallado sobre las cabeceras de Bitmap de las versiones 2, 3 y 4. Tambien he encontrado informacion sobre compresion RLE4 y RLE8.

Lo que no he conseguido encontrar es infomracion sobre la compresion con campos de bits (tipo de compresion 3 para la version 3 en winNT), o la estructura de la informacion de los pixeles en si para pixeles codificados con 1, 4, 8 o 32 bits.

Si alguien sabe donde podria encontrar esta informacion, o pudiese dejarme algun enlace a algun manual o descripcion sobre la estructura interna y tipos de compresion en el formato bmp, se lo agradecria mucho.

¡Muchas gracias de antemano y un saludo!

PD: Se me ha ocurrido que si despues de un tiempo, no hubiese respuesta, igual se podria trasladar el post al subforo seguridad/criptografia.  El moderador decide.

¡Saludos!
56  Programación / Scripting / ¿Se pueden crear ejecutables con ‭‬javascript? [SOLUCIONADO] en: 21 Diciembre 2009, 01:55 am
Muy buenas a todos.

En el warzone una de las pruebas de programacion (mas de una), habla de crear un programa en ‭‬javascript que lleve a cabo una determinada tarea. Como nunca habia programado en ‭‬javascript, he estado buscando nformacion a cerca del lenguaje. Yo pensaba que solo se podia utilizar incrustado en paginas web, para hacerlas dinamicas y añadir funcionalidades, pero tal y como esta planteado el ejercicio, da la impresion de que tambien se pueden crear aplicaciones en JS. ¿Es asi?

¿De todas formas, sabeis de algun buen manual o tutorial sobre el tema?

¡Un saludo y muchas gracias!

Solucionado: No se trataba de generar un ejecutable, ni de crear una funcion ni nada por el estilo. Solo habia que poner el codigo que solucionaba el problema. Asi, sin mas, suelto, sin incluirlo en el esqueleto de una web ni nada.

¡Muchas gracias!
57  Seguridad Informática / WarZone / Criptografia - Basico en: 13 Diciembre 2009, 21:08 pm
Hola a todos!

La respuesta a criptografia-basico es el mensaje descifrado, o algo que tiene que ver con numero o palabra descrito en el mensaje descifrado?

¿O lo he descifrado mal?

Estoy desorientado, no pido la resolucion, solo saber si voy por buen camino...

Saludos!
58  Seguridad Informática / WarZone / primos.wz en: 9 Diciembre 2009, 01:46 am
Buenas, tengo un pseudocodigo que traducido al codigo ensamblador de la maquina virtual me da resultado que se quiere, pero como no tenia ni idea de ASM, usando el sentido comun me sale un codigo de mas de 25 lineas de codigo.

No pongo aqqui el code, porque seria ponoer "la solucion" para todo el mundo. Solo queria preguntar si hay algunas pautas basicas que se puedan seguir para compactar el codigo.

Si hace falta enviaros el codigo a algun sitio oficial para tratar estos temas, me lo decis y lo envio, (se que he visto por ahi alguna direccion de correo electronico...)

¡¡¡¡Saludos!!!!
59  Programación / Programación C/C++ / Para que dejeis de preguntar de una vez por los menus en: 3 Diciembre 2009, 19:58 pm
MODIFICADO

Añadido:
----------
- portabilidad
- texto centrado


Ya se que todo el codigo que viene a continuacion es una morcilla de las de Burgos, pero si mirais el ejemplo que hay al final, vereis como se simplifica todo a la hora de utilizar los menus.

Menu.h
Código
  1.  
  2. /*
  3.  
  4. IMPORTANTE: Cuando el menu ya no sea util, ultilizar siempre la funcion finalizarMenu
  5.  
  6. */
  7.  
  8. #ifndef MENU_H
  9. #define MENU_H
  10.  
  11. #include <stdio.h>
  12. #include <stdlib.h>
  13. #include <string.h>
  14.  
  15. /*
  16. * Para compilar en UNIX desactivar la definicion de WINDOWS y activar la de UNIX
  17. * Para otros SO desactivar ambas.
  18. */
  19.  
  20. #ifndef WINDOWS
  21.    #define WINDOWS
  22. #endif
  23. /*
  24. #ifndef UNIX
  25.     #define UNIX
  26. #endif
  27. */
  28.  
  29. #define MENU_OK                   0L
  30. #define MENU_ERROR                1L
  31. #define MENU_MEMORIA_INSUFICIENTE 2L
  32.  
  33. #define MENU_MODO_NORMAL      0L
  34. #define MENU_MODO_ULTIMA_CERO 1L
  35.  
  36. struct Menu
  37. {
  38.    char*         titulo;
  39.    char*         texto;
  40.    char**        opciones;
  41.    int           numOpciones; /* numero de opciones */
  42.    int           maxlen;
  43.    unsigned long lFlags;
  44. };
  45. typedef struct Menu Menu;
  46.  
  47. void inicializarMenu(Menu* menu,char* titulo, char* texto, char* opciones[]);
  48. int mostrarMenu(Menu* menu,int modo);
  49. void finalizarMenu(Menu* menu);
  50.  
  51. #endif /* MENU_H */
  52.  

menu.c
Código
  1. #include "menu.h"
  2.  
  3. void inicializarMenu(Menu* menu, char* titulo, char* texto, char* opciones[])
  4. {
  5.   menu->lFlags = MENU_OK;
  6.  
  7.   if(titulo == NULL)
  8.      menu->titulo = NULL;
  9.   else
  10.   {
  11.       if((menu->titulo = (char*) malloc(strlen(titulo) * sizeof(char) + 1)))
  12.           strcpy(menu->titulo,titulo);
  13.      else
  14.      {
  15.         menu->lFlags |= MENU_ERROR | MENU_MEMORIA_INSUFICIENTE;
  16.         menu->texto = NULL;
  17.      }
  18.   }
  19.  
  20.   if(texto == NULL)
  21.      menu->texto = NULL;
  22.   else
  23.   {
  24.      if((menu->texto = (char*) malloc(strlen(texto) * sizeof(char) + 1)))
  25.         strcpy(menu->texto,texto);
  26.      else
  27.      {
  28.         menu->lFlags |= MENU_ERROR | MENU_MEMORIA_INSUFICIENTE;
  29.         menu->texto = NULL;
  30.  
  31.         if(menu->titulo)
  32.         {
  33.            free(menu->titulo);
  34.            menu->titulo = NULL;
  35.         }
  36.      }
  37.   }
  38.  
  39.   if(opciones == NULL)
  40.      menu->opciones = NULL;
  41.   else
  42.   {
  43.      menu->numOpciones = -1;
  44.  
  45.      while(opciones[ ++(menu->numOpciones) ][0]);
  46.  
  47.      if((menu->opciones = (char**) malloc(menu->numOpciones * sizeof(char*))))
  48.      {
  49.         int i=0;
  50.  
  51.         menu->maxlen = 0;
  52.  
  53.         for(i=0 ; i < menu->numOpciones ; i++)
  54.         {
  55.            if
  56.            (
  57.               (menu->opciones[i] =
  58.               (char*) malloc(strlen(opciones[i]) * sizeof(char) + 1))
  59.            )
  60.            {
  61.               strcpy(menu->opciones[i],opciones[i]);
  62.  
  63.               if(strlen(menu->opciones[i]) > menu->maxlen)
  64.                  menu->maxlen = strlen(menu->opciones[i]);
  65.            }
  66.            else
  67.            {
  68.               int j;
  69.  
  70.               for(j=0 ; j<i ; j++)
  71.                  free(menu->opciones[j]);
  72.  
  73.               free(menu->opciones);
  74.  
  75.               menu->opciones = NULL;
  76.               menu->numOpciones = 0;
  77.  
  78.               if(menu->texto)
  79.               {
  80.                  free(menu->texto);
  81.                  menu->texto = NULL;
  82.               }
  83.  
  84.               if(menu->titulo)
  85.               {
  86.                  free(menu->titulo);
  87.                  menu->titulo = NULL;
  88.               }
  89.  
  90.               menu->lFlags |= MENU_ERROR | MENU_MEMORIA_INSUFICIENTE;
  91.            }
  92.         }
  93.      }
  94.      else
  95.      {
  96.         menu->lFlags |= MENU_ERROR | MENU_MEMORIA_INSUFICIENTE;
  97.         menu->opciones = NULL;
  98.  
  99.         if(menu->texto)
  100.         {
  101.            free(menu->texto);
  102.            menu->texto = NULL;
  103.         }
  104.  
  105.         if(menu->titulo)
  106.         {
  107.            free(menu->titulo);
  108.            menu->titulo = NULL;
  109.         }
  110.      }
  111.   }
  112. }
  113.  
  114. int mostrarMenu(Menu* menu,int modo)
  115. {
  116.   int i=0, opcion=0;
  117.  
  118.   if(menu->numOpciones)
  119.   {
  120.       do{
  121.  
  122.          #ifdef WINDOWS
  123.             system("CLS");
  124.          #elif defined UNIX
  125.             system("clear");
  126.          #endif
  127.  
  128.          if(menu->titulo)
  129.          {
  130.             for(i=0 ; i<80 ; i++)
  131.                 printf("=");
  132.  
  133.             printf("%*s\n", 40 + strlen(menu->titulo) / 2, , menu->titulo);
  134.  
  135.             for(i=0 ; i<80 ; i++)
  136.                 printf("=");
  137.          }
  138.  
  139.          if(menu->texto)
  140.              printf("%-*s%s\n\n",40 - menu->maxlen / 2 - 8, " ", menu->texto);
  141.  
  142.          for(i=0 ; i < menu->numOpciones ; i++)
  143.          {
  144.             if(modo == MENU_MODO_ULTIMA_CERO && i == menu->numOpciones - 1)
  145.             {
  146.                if(menu->numOpciones > 10)
  147.                   printf("%*s 0. %-*s\n", 40 - menu->maxlen / 2 - 4, " ", 40 + menu->maxlen / 2,
  148.                                            menu->opciones[i]);
  149.                else
  150.                   printf("%*s0. %-*s\n", 40 - menu->maxlen / 2 - 4, " ", 40 + menu->maxlen / 2,
  151.                                           menu->opciones[i]);
  152.             }
  153.             else
  154.             {
  155.                if(modo != MENU_MODO_ULTIMA_CERO)
  156.                {
  157.                   if(menu->numOpciones >= 10)
  158.                   {
  159.                      printf("%*s%2d. %-*s\n", 40 - menu->maxlen / 2 - 4, " ", i+1,
  160.                                                40 + menu->maxlen / 2, menu->opciones[i]);
  161.                   }
  162.                   else
  163.                   {
  164.                      printf("%*s%d. %-*s\n", 40 - menu->maxlen / 2 - 4, " ", i+1,
  165.                                               40 + menu->maxlen / 2, menu->opciones[i]);
  166.                   }
  167.                }
  168.                else
  169.                {
  170.                   if(menu->numOpciones > 10)
  171.                   {
  172.                      printf("%*s%2d. %-*s\n", 40 - menu->maxlen / 2 - 4, " ", i+1,
  173.                                                40 + menu->maxlen / 2, menu->opciones[i]);
  174.                   }
  175.                   else
  176.                   {
  177.                      printf("%*s%d. %-*s\n", 40 - menu->maxlen / 2 - 4, " ", i+1,
  178.                                               40 + menu->maxlen / 2, menu->opciones[i]);
  179.                   }
  180.                }
  181.             }
  182.          }
  183.          printf("%-*s>", 40 - menu->maxlen / 2 - 8, " ");
  184.  
  185.         if(!scanf("%d", &opcion))
  186.         {
  187.            while(getchar() != '\n');
  188.            scanf("%d", &opcion);
  189.         }        
  190.  
  191.       }while
  192.       (
  193.          (
  194.             (modo != MENU_MODO_ULTIMA_CERO) ? (opcion < 1) : (opcion < 0)
  195.          )
  196.          ||
  197.          (
  198.             (modo != MENU_MODO_ULTIMA_CERO) ?
  199.                (opcion >  menu->numOpciones)
  200.                :
  201.                (opcion >= menu->numOpciones)
  202.          )
  203.       );
  204.  
  205.       return opcion;
  206.   }
  207.  
  208.   return -1;
  209. }
  210.  
  211. void finalizarMenu(Menu* menu)
  212. {
  213.   if(menu->texto)
  214.   {
  215.      free(menu->texto);
  216.      menu->texto = NULL;
  217.   }
  218.  
  219.   if(menu->opciones)
  220.   {
  221.       int i=0;
  222.  
  223.       for(i=0 ; i < menu->numOpciones ; i++)
  224.       {
  225.           if(menu->opciones[i])
  226.           {
  227.               free(menu->opciones[i]);
  228.               menu->opciones[i] = NULL;
  229.           }
  230.       }
  231.       free(menu->opciones);
  232.       menu->opciones = NULL;
  233.   }
  234.  
  235.   menu->numOpciones = 0;
  236. }
  237.  

ejemplo: (tener en cuenta que menu.h y menu.c tienen que estar en el mismo directorio donde este el fichero con la funcion principal)
Código
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3.  
  4. #include "menu.h"
  5. #include "menu.c" /* solo si no se va crear un proyecto*/
  6.  
  7. int main(int argc, char *argv[])
  8. {
  9.    Menu menu;
  10.    char *opciones[] = {"Continuar","Salir",""}; /* la ultima opcion siempre es una cadena vacia*/
  11.    int opcion;
  12.  
  13.    inicializarMenu(&menu,"MENU DE PRUEBA","Escoger una opcion:",opciones);
  14.  
  15.    do{
  16.        opcion=mostrarMenu(&menu,MENU_MODO_ULTIMA_CERO);
  17.  
  18.        switch(opcion)
  19.        {
  20.            case 1:
  21.                printf("Has escogido continuar.\n");
  22.                break;
  23.            case 0:
  24.                printf("Has escogido salir.\n");
  25.                break;
  26.        }
  27.    }while(opcion!=0);
  28.  
  29.    /* ¡¡¡¡¡IMPORTANTE!!!!! */
  30.    finalizarMenu(&menu);
  31.  
  32.    return 0;
  33. }
  34.  

En lo que es el menu, me gustaria completar alguna opcion mas, pero el codigo ya es funcional.

Si el codigo no funciona, avisad, ya que es una adaptacion del codigo que realmente tengo guardado. Lo he puesto asi para evitar incluir mas headers y mas codigo.

El codigo de ejemplo lo he imprivosado sobre la marcha. Si hubiese algun error revisad el codigo, que no creo que haya darle muchas vueltas.
Páginas: 1 2 3 4 5 [6]
WAP2 - Aviso Legal - Powered by SMF 1.1.21 | SMF © 2006-2008, Simple Machines