Foro de elhacker.net

Programación => Programación C/C++ => Mensaje iniciado por: Fedex15 en 21 Abril 2017, 00:25 am



Título: Listas generales en C
Publicado por: Fedex15 en 21 Abril 2017, 00:25 am
Hola, estoy tratando de resolver un problema que se me pide con listas. Debo usar listas siguiendo la sig estructura:

Código
  1. typedef struct _GNodo {
  2. void *dato;
  3. struct _GNodo *sig;
  4. } GNodo;

Tengo 3 archivos, el glist.h, glist.c, y main_g.c. Las funciones que tengo son las basicas, que me permiten crear, destruir, agregar final, agregar inicio, y recorrer.

Código
  1. #include "glist.h"
  2. #include <stdlib.h>
  3.  
  4. GList glist_crear() {
  5. return NULL;
  6. }
  7.  
  8. void glist_destruir(GList lista) {
  9. GList nodoAEliminar;
  10. while (lista != NULL) {
  11. nodoAEliminar = lista;
  12. lista = lista->sig;
  13. free (nodoAEliminar);
  14. }
  15. }
  16.  
  17. GList glist_agregar_inicio(GList lista, void* dato) {
  18. GList nuevoNodo = malloc (sizeof (GNodo));
  19. nuevoNodo->dato = dato;
  20. nuevoNodo->sig = lista;
  21. return nuevoNodo;
  22. }
  23.  
  24. GList glist_agregar_final (GList lista, void* dato) {
  25. GList nuevoNodo = malloc (sizeof (GNodo));
  26. nuevoNodo->dato = dato;
  27. nuevoNodo->sig = NULL;
  28. if (lista == NULL) {
  29. return nuevoNodo;
  30. }
  31. GList nodo = lista;
  32. for (; nodo->sig != NULL; nodo = nodo->sig);
  33. nodo->sig = nuevoNodo;
  34. return lista;
  35. }
  36.  
  37. void glist_recorrer (GList lista, FuncionVisitante visit) {
  38. for (GList nodo = lista; nodo != NULL; nodo=nodo->sig){
  39. visit (nodo->dato);
  40. }
  41. }
  42.  

El problema que estoy teniendo, es que ahora no estoy trabajando con listas donde el dato es un int, estoy trabajando con listas donde el dato es un puntero a void. Yo se que el puntero a void puede apuntar a cualquier tipo de dato, pero para poder desreferenciarlo tengo que aplicar el cast correspondiente al tipo de dato que guarda.

Código
  1. int main(){
  2. int a = 3;
  3. void *ptr;
  4. ptr = &a;
  5. printf ("%d", *((int*)ptr)); //convierto ptr a int* y lo desreferencio con *
  6. return 0;
  7. }
  8.  

Este es el main que tengo:

Código
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <string.h>
  4. #include "glist.h"
  5.  
  6.  
  7. static void imprimir_entero(void* dato) {
  8.  printf("%d ", (*(int*)dato));
  9. }
  10.  
  11. int main(){
  12. int a = 3;
  13. char c = 'a';
  14. double d = 2.3;
  15. GList lista = glist_crear();
  16. lista = glist_agregar_inicio (lista, &a);
  17. lista = glist_agregar_inicio (lista, &c);
  18. lista = glist_agregar_inicio (lista, &d);
  19. lista = glist_agregar_final (lista, &a);
  20. printf ("%lf %c %d\n", (*(double*)lista->dato),(*(char*)lista->sig->dato),(*(int*)lista->sig->sig->sig->dato) );
  21. //~ printf ("\n %lf", (*(double*)(lista->dato)));
  22. glist_recorrer (lista, imprimir_entero);
  23. glist_destruir (lista);
  24. return 0;
  25. }
  26.  

Como podria hacer para poder saber cual es el tipo de dato al que apunta void. Como podria hacer lo que esta en la linea 20

Código
  1. printf ("%lf %c %d\n", (*(double*)lista->dato),(*(char*)lista->sig->dato),(*(int*)lista->sig->sig->sig->dato) );

de forma general con la funcion imprimir_entero. El primer caso seria con un entero, un double y un char o cadena. Gracias


Título: Re: Listas generales en C
Publicado por: MAFUS en 21 Abril 2017, 11:08 am
He intentado compilar el código y tiene muchos errores. Podría resolverlos pero, la verdad, me ha dado pereza. Si pudieras corregir el código para que no haya mas errores que el lógico que intentas resolver sería de agradecer.


Título: Re: Listas generales en C
Publicado por: Fedex15 en 21 Abril 2017, 15:00 pm
He intentado compilar el código y tiene muchos errores. Podría resolverlos pero, la verdad, me ha dado pereza. Si pudieras corregir el código para que no haya mas errores que el lógico que intentas resolver sería de agradecer.

Gracias por responder, aca te dejo todo el codigo en un solo archivo:

Código
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <stddef.h>
  4.  
  5. typedef struct _GNodo {
  6. void *dato;
  7. struct _GNodo *sig;
  8. } GNodo;
  9.  
  10. typedef GNodo* GList;
  11.  
  12. typedef void (*FuncionVisitante) (void* dato);
  13.  
  14. GList glist_crear() {
  15. return NULL;
  16. }
  17.  
  18. void glist_destruir(GList lista) {
  19. GList nodoAEliminar;
  20. while (lista != NULL) {
  21. nodoAEliminar = lista;
  22. lista = lista->sig;
  23. free (nodoAEliminar);
  24. }
  25. }
  26.  
  27. GList glist_agregar_inicio(GList lista, void* dato) {
  28. GList nuevoNodo = malloc (sizeof (GNodo));
  29. nuevoNodo->dato = dato;
  30. nuevoNodo->sig = lista;
  31. return nuevoNodo;
  32. }
  33.  
  34. GList glist_agregar_final (GList lista, void* dato) {
  35. GList nuevoNodo = malloc (sizeof (GNodo));
  36. nuevoNodo->dato = dato;
  37. nuevoNodo->sig = NULL;
  38. if (lista == NULL) {
  39. return nuevoNodo;
  40. }
  41. GList nodo = lista;
  42. for (; nodo->sig != NULL; nodo = nodo->sig);
  43. nodo->sig = nuevoNodo;
  44. return lista;
  45. }
  46.  
  47. void glist_recorrer (GList lista, FuncionVisitante visit) {
  48. for (GList nodo = lista; nodo != NULL; nodo=nodo->sig){
  49. visit (nodo->dato);
  50. }
  51. }
  52.  
  53. static void imprimir_entero(void* dato) {
  54.  printf("%d ", (*(int*)dato));
  55. }
  56. // Lo que yo quiero hacer es algo asi, pero no se como hacerlo:
  57. // Si es entero:
  58. // printf ("%d ", (*(int*)dato));
  59. // Si es double:
  60. //  printf ("%lf ", (*(double*)dato));
  61. // Si es cadena:
  62. //  printf ("%s ", (*(char*)dato));
  63.  
  64. // Como tendria que hacer para saber de alguna forma cual es el tipo
  65. // que esta apuntando el puntero.
  66.  
  67. int main(){
  68. int a = 3;
  69. char c = 'a';
  70. double d = 2.3;
  71. GList lista = glist_crear();
  72. lista = glist_agregar_inicio (lista, &a);
  73. lista = glist_agregar_inicio (lista, &c);
  74. lista = glist_agregar_inicio (lista, &d);
  75. lista = glist_agregar_final (lista, &a);
  76. printf ("%lf %c %d\n", (*(double*)lista->dato),(*(char*)lista->sig->dato),(*(int*)lista->sig->sig->sig->dato) );
  77. glist_recorrer (lista, imprimir_entero);
  78. glist_destruir (lista);
  79. return 0;
  80. }
  81.  

yo lo compilo usando gcc listas.c -o lista, asi me funciona bien.

PD: Aca te dejo unas modificaciones que estuve haciendo, haber si voy por buen camino.

Código
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <stddef.h>
  4.  
  5. #define ENTERO 0
  6. #define DOUBLE 1
  7. #define CADENA 2
  8.  
  9. typedef struct _variante{
  10. int tipo;
  11. void* valor;
  12. } variante;
  13.  
  14. typedef variante* dato;
  15.  
  16. typedef struct _GNodo {
  17. void *dato;
  18. struct _GNodo *sig;
  19. } GNodo;
  20.  
  21. typedef GNodo* GList;
  22.  
  23. typedef void (*FuncionVisitante) (dato d);
  24.  
  25. dato dato_crear (){
  26. dato nuevoDato = (variante*) malloc (sizeof(variante));
  27. return nuevoDato;
  28. }
  29.  
  30. void dato_insertar (dato datos, int tipo, void* d){
  31. datos->tipo = tipo;
  32. datos->valor = d;
  33. }
  34.  
  35.  
  36. void glist_destruir(GList lista) {
  37. GList nodoAEliminar;
  38. while (lista != NULL) {
  39. nodoAEliminar = lista;
  40. lista = lista->sig;
  41. free (nodoAEliminar);
  42. }
  43. }
  44.  
  45. GList glist_crear() {
  46. return NULL;
  47. }
  48.  
  49. GList glist_agregar_inicio(GList lista, void* dato) {
  50. GList nuevoNodo = malloc (sizeof (GNodo));
  51. nuevoNodo->dato = dato;
  52. nuevoNodo->sig = lista;
  53. return nuevoNodo;
  54. }
  55.  
  56. GList glist_agregar_final (GList lista, void* dato) {
  57. GList nuevoNodo = malloc (sizeof (GNodo));
  58. nuevoNodo->dato = dato;
  59. nuevoNodo->sig = NULL;
  60. if (lista == NULL) {
  61. return nuevoNodo;
  62. }
  63. GList nodo = lista;
  64. for (; nodo->sig != NULL; nodo = nodo->sig);
  65. nodo->sig = nuevoNodo;
  66. return lista;
  67. }
  68.  
  69. void glist_recorrer (GList lista, FuncionVisitante visit) {
  70. for (GList nodo = lista; nodo != NULL; nodo=nodo->sig){
  71. visit (nodo->dato);
  72. }
  73. }
  74.  
  75. static void imprimir_entero(dato d) {  
  76.  switch (d->tipo){
  77. case (0):
  78. printf ("%d ", *(int*)d->valor);
  79. break;
  80. case (1):
  81. printf ("%lf ", *(double*)d->valor);
  82. break;
  83. case (2):
  84. printf ("%s ", (char*)d->valor);
  85. break;
  86. }
  87. }
  88. // Lo que yo quiero hacer es algo asi, pero no se como hacerlo:
  89. // Si es entero:
  90. // printf ("%d ", (*(int*)dato));
  91. // Si es double:
  92. //  printf ("%lf ", (*(double*)dato));
  93. // Si es cadena:
  94. //  printf ("%s ", (*(char*)dato));
  95.  
  96. // Como tendria que hacer para saber de alguna forma cual es el tipo
  97. // que esta apuntando el puntero.
  98.  
  99. int main(){
  100. int b = 3;
  101. double num = 6.7;
  102. char cadena[50] = "mundo ";
  103. dato general = dato_crear();
  104. dato general2 = dato_crear();
  105. dato general3 = dato_crear();
  106. dato_insertar(general, ENTERO, &b);
  107. dato_insertar(general2, DOUBLE, &num);
  108. dato_insertar(general3, CADENA, cadena);
  109. GList lista = glist_crear();
  110. lista = glist_agregar_inicio (lista, general);
  111. lista = glist_agregar_inicio (lista, general2);
  112. lista = glist_agregar_inicio (lista, general3);
  113. //~ lista = glist_agregar_final (lista, &a);
  114. glist_recorrer (lista, imprimir_entero);
  115. glist_destruir (lista);
  116. return 0;
  117. }
  118.  


Título: Re: Listas generales en C
Publicado por: MAFUS en 21 Abril 2017, 19:30 pm
No puedes, C no proporciona reflexión.

Pero gcc sí proporciona, gracias a una extensión del compilador, un operador typeof que sirve para obtener el tipo de un dato. https://gcc.gnu.org/onlinedocs/gcc/Typeof.html

Por otra parte el estándar C11 tiene funciones genéricas las cuales se configuran mediante una macro especial. http://blog.smartbear.com/codereviewer/c11-a-new-c-standard-aiming-at-safer-programming/



Una forma de hacerlo es como la que estás realizando, marcar qué tipo de dato va a guardar la variable dato. Es decir, hacerlo a mano.


Título: Re: Listas generales en C
Publicado por: CalgaryCorpus en 24 Abril 2017, 01:41 am
Cuando estas insertando, en estos momentos, con el codigo que presentas, indicas explicitamente el tipo de dato que esta siendo guardado., por ejemplo:

Código:
dato_insertar(general, ENTERO, &b);
dato_insertar(general2, DOUBLE, &num);
dato_insertar(general3, CADENA, cadena);
luego te obligas a hacer un switch con ese tipo.

Tengo una solucion que elimina el switch posterior, aunque aun tienes que indicar el tipo de dato que usas al insertar.

Sugiero crear un nuevo campo que guarde un puntero a una funcion que sepa imprimir el dato que estas agregando.

Código:
typedef struct _variante{
int tipo;
void* valor;
        void (*f)(void *);
       
} variante;
definir las funciones que saben imprimir, por ejemplo:

Código:
void tipo_entero(void * pTipo) {
  printf("%d", *(int *)pTipo );
}

Las otras funciones son similares, pero usan otros %d en el printf y otro cast.

luego en dato_insertar simplemente se copia el puntero a funcion

Código:
void dato_insertar (dato datos, int tipo, void* d, void (*f)() ){
datos->tipo = tipo;
datos->valor = d;
        datos->f = f;
}
y esta funcion se invoca usando el nombre de la funcion a usar para imprimr. Nota que es solo el nombre de la funcion, No como siempre usas las funciones, con parentesis.

Código:
dato_insertar(general, &b, tipo_entero );
   
y finalmente cuando quieras imprimir el dato, solo haces

Código:
dato.f(dato.valor);

y esto imprimira de acuerdo a la funcion que usaste.

Suerte!


Título: Re: Listas generales en C
Publicado por: Fedex15 en 24 Abril 2017, 02:59 am
Cuando estas insertando, en estos momentos, con el codigo que presentas, indicas explicitamente el tipo de dato que esta siendo guardado., por ejemplo:

Código:
dato_insertar(general, ENTERO, &b);
dato_insertar(general2, DOUBLE, &num);
dato_insertar(general3, CADENA, cadena);
luego te obligas a hacer un switch con ese tipo.

Tengo una solucion que elimina el switch posterior, aunque aun tienes que indicar el tipo de dato que usas al insertar.

Sugiero crear un nuevo campo que guarde un puntero a una funcion que sepa imprimir el dato que estas agregando.

Código:
typedef struct _variante{
int tipo;
void* valor;
        void (*f)(void *);
       
} variante;
definir las funciones que saben imprimir, por ejemplo:

Código:
void tipo_entero(void * pTipo) {
  printf("%d", *(int *)pTipo );
}

Las otras funciones son similares, pero usan otros %d en el printf y otro cast.

luego en dato_insertar simplemente se copia el puntero a funcion

Código:
void dato_insertar (dato datos, int tipo, void* d, void (*f)() ){
datos->tipo = tipo;
datos->valor = d;
        datos->f = f;
}
y esta funcion se invoca usando el nombre de la funcion a usar para imprimr. Nota que es solo el nombre de la funcion, No como siempre usas las funciones, con parentesis.

Código:
dato_insertar(general, &b, tipo_entero );
   
y finalmente cuando quieras imprimir el dato, solo haces

Código:
dato.f(dato.valor);

y esto imprimira de acuerdo a la funcion que usaste.

Suerte!


Gracias por la respuesta, voy a ver que me sale. Ahora estoy haciendo un menu que me pidieron usando listas generales para crear listas de enteros.


Título: Re: Listas generales en C
Publicado por: CalgaryCorpus en 24 Abril 2017, 03:04 am
La firma de la funcion modificada que propuse esta mala, o incompleta.

Dice

Código:
void dato_insertar (dato datos, int tipo, void* d, void (*f)() ){

deberia decir

Código:
void dato_insertar (dato datos, void* d, void (*f)(void *) ){