¿Buenos ejemplos usando ‘union’ con ‘enum’?

Lo que trato de entender lo siguiente:

‘Unión’ actúa como una variable y puedo almacenar valor en ella de diferentes tipos. ¿Qué pasa si en el valor almacenado de tipo ‘float’, pero lo leo como ‘corto’?

¿Hay alguna manera de estar seguro del tipo de valor que voy a recuperar de la unión?

¿Cómo puedo lidiar con el tipo de casos?

Vi una nota en alguna parte, habló sobre el uso de ‘enumeración’ para casos como este, pero no había una explicación lo suficientemente buena de por qué.

¿Podría explicar por qué es útil / seguro usar ‘uniones’ con ‘enums’, por favor? O mostrar algunos ejemplos.

Gracias de antemano, Nick.

Creo que es una forma de implementar uniones etiquetadas o tipos de sum . Por ejemplo, en C99 usando una unión anónima

enum kind_en { knothing, kint, kfloat, kstring }; struct value_st { enum kind_en kind; union { int n; // when kint float f; // when kfloat char* s; // when kstring }; }; 

entonces por ejemplo

 void print_value (struct value_st* v) { if (!v) {puts("nil"); return; }; switch (v->kind) { case knothing: puts("nothing"); return; case kint: printf("int#%d", v->n); return; case kfloat: printf("float#%g", v->f); return; case kstring: printf("string'%s'", v->s); return; default: abort(); } } struct value_st* make_int_value(int i) { struct value_st* val = malloc(sizeof(struct value_st)); if (!val) { perror("malloc int value"); exit(EXIT_FAILURE); }; val->kind = kint; val->n = i; return val; } 

Un ejemplo mucho más antiguo del siglo anterior es el tipo XEvent de Xlib

Tenga en cuenta que algunos lenguajes de progtwigción tienen una forma más fácil de admitir tipos de sum. En Ocaml solo necesitas

 type val_t = Knothing | Kint of int | Kfloat of float | Kstring of string;; 

y lo más importante es que tienes un patrón de coincidencia

Respuestas a tus preguntas:

  1. Sí, codifique el tipo en una estructura que contenga la unión:

     union { float f; int i; } my_union; enum { its_a_float, its_an_int } flavor; struct { flavor x; my_union u; } data_blob; 
  2. No estoy seguro de que respondo a tu pregunta, ¿qué tipo de casos?

  3. véase más arriba

  4. Es útil para cuando no conoce los datos exactos que tiene / necesita en el momento de la comstackción y necesita manejar varios tipos de los mismos datos lógicos.

Agregar nuevo archivo de enumeración de tipo y mantener allí la información sobre el tipo actual

 #include  #include  #include  typedef enum types_tag { CHAR, INT, FLOAT } types_t; typedef union value_tag { char c; int i; float f; } value_t; typedef struct store_tag { types_t type; value_t value; } store_t; void printValue(const store_t *o) { switch (o->type) { case CHAR: printf("%c\n", o->value.c); break; case INT: printf("%d\n", o->value.i); break; case FLOAT: printf("%.3f", o->value.f); break; default: exit(EXIT_FAILURE); } return; } void main() { store_t a; a.type = CHAR; a.value.c = 'A'; printValue(&a); a.type = FLOAT; a.value.f = 10.45; printValue(&a); _getch(); } 

Además de eso, puede mantener la información solo en un montón de memoria y usar void *

 #include  #include  #include  typedef enum types_tag { CHAR, INT, FLOAT } types_t; typedef struct store_tag { types_t type; void* value; } store_t; void printValue(const store_t *o) { switch (o->type) { case CHAR: printf("%c\n", *(char*)(o->value)); break; case INT: printf("%d\n", *(int*)(o->value)); break; case FLOAT: printf("%.3f", *(float*)(o->value)); break; default: exit(EXIT_FAILURE); } return; } void main() { store_t a; a.type = CHAR; a.value = malloc(1); *((char*) a.value) = 'A'; printValue(&a); free(a.value); a.type = FLOAT; a.value = malloc(sizeof(float)); *((float*) a.value) = 34.7; printValue(&a); free(a.value); _getch(); } 

y agregar algunas funciones para ocultar la creación y eliminación de variables.