2014-09-19 3 views
0

Что я пытаюсь понять:Хорошие примеры использования 'union' с 'enum'?

«Союз» действует как переменная, и я могу хранить в нем значение разных типов. Что делать, если в нем хранится значение типа «float», но я прочитал его как «короткий»?

Есть ли способы, как я могу быть уверен в типе ценности, которую я собираюсь извлечь из объединения?

Как я могу справиться с конкретными случаями?

Я где-то видел записку, Он говорил об использовании «перечисления» для таких случаев, но не было достаточного объяснения почему?

Не могли бы вы объяснить, почему это полезно/безопасно, используя «союзы» с «перечислениями», пожалуйста? Или показать несколько примеров.

Заранее спасибо, Ник.

+0

Даже нет членов в боковом соединении. Только для одного члена союз распределяет базу памяти, размер элемента которой большой. Используя эту память, вы можете получить доступ к любому члену в этом объединении. Поскольку он выделяет большую память на основе member.and также в союзе возможен только один доступный одновременно. Нет проблем и всего. –

+0

«Что, если в нем хранится значение типа« float », но я прочитал его как« короткий »? - неопределенное поведение. «Есть ли способы, как я могу быть уверен в типе ценности, которую я собираюсь извлечь из союза?» - компьютеры имеют память; используйте некоторые из них, чтобы представить, какой тип значения в союзе. Перечисление - это один из способов представления того, какой тип хранится. –

ответ

3

Я считаю, что это способ реализации tagged unions или суммы типов. . в C99 с использованием anonymous union

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 
    }; 
}; 

затем например

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; 
} 

Гораздо старше пример из прошлого века является XEvent типа Xlib

Обратите внимание, что некоторые языки программирования имеют более простой способ поддержки типов сумм. В OCaml вам нужно просто

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

и самое главном у вас есть pattern matching

1

Ответов на вопросы:

  1. Да, кодирующий типа в структуру, которая содержит объединение:

    union { 
        float f; 
        int i; 
    } my_union; 
    
    enum { 
        its_a_float, 
        its_an_int 
    } flavor; 
    
    struct { 
        flavor x; 
        my_union u; 
    } data_blob; 
    
  2. Не уверен, что я задаюсь вопросом, какие случаи?

  3. см.выше

  4. Это полезно, когда вы не знаете точных данных у вас есть/необходимость во время компиляции и нужно работать с несколькими вкусами тех же логических данных.

1

Добавить подал типа перечислимого и держать там информацию о типе текущего

#include <stdio.h> 
#include <conio.h> 
#include <stdlib.h> 

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(); 
} 

Кроме этого, вы можете сохранить информацию только в кучу памяти и использовать пустоты *

#include <stdio.h> 
#include <conio.h> 
#include <stdlib.h> 

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(); 
} 

и добавить некоторые функции, чтобы скрыть создание и удаление переменных.

+0

Не так ли, как мой ответ (за исключением использования анонимных союзов, что удобно в этом случае)? –

+0

Может быть, просто писал долгое время и видел его после публикации. –

+0

Случается ко мне все время .... Один момент, 'main' имеет тип' int' и возвращает значение. Просто потому, что ваш компилятор позволит вам уйти с «void» - нет. :) –

Смежные вопросы