2009-04-07 4 views
31

Я ищу примеры для объединения, чтобы не понимать, как работает профсоюз, надеюсь, что я это сделаю, но чтобы увидеть, какие люди взломают с профсоюзом.Примеры Union in C

Так что не стесняйтесь поделиться ваш профсоюз взломать (с некоторыми пояснениями, конечно :))

ответ

34

Классический будет представлять значение «неизвестного» типа, как и в ядре упрощенной виртуальной машины:

typedef enum { INTEGER, STRING, REAL, POINTER } Type; 

typedef struct 
{ 
    Type type; 
    union { 
    int integer; 
    char *string; 
    float real; 
    void *pointer; 
    } x; 
} Value; 

Используя это, вы можете написать код, который обрабатывает «ценность», не зная их точный тип , например, реализовать стек и так далее.

Поскольку это (старый, pre-C11) C, внутреннему соединению должно быть присвоено имя поля во внешнем struct. В C++ вы можете позволить анонимным union. Выбор этого имени может быть трудным. Я имею тенденцию идти с чем-то однобуквенным, поскольку он почти никогда не упоминается в изоляции, и поэтому из контекста всегда ясно, что происходит.

Код для установки значения в целое число может выглядеть следующим образом:

Value value_new_integer(int v) 
{ 
    Value v; 
    v.type = INTEGER; 
    v.x.integer = v; 
    return v; 
} 

Здесь я использую тот факт, что struct s может быть возвращен непосредственно, и обрабатывает почти как значения примитивного типа (можно назначить struct s).

+0

Я предполагаю это может быть полезно! – claf

+1

Обратите внимание, что C11 предоставляет анонимные объединения, поэтому параграф «поскольку это C» применяется к C90 и C99, но не к C11.Точно так же, поскольку этот ответ был написан в 2009 году, было вполне разумно не прогнозировать, что даст C11. –

+1

@unwind из любопытства, я задаю этот вопрос. Вызывает ли указанная выше функция _redeclaration error_? –

9

Вот маленький один я использую каждый день:

struct tagVARIANT { 
    union { 
     struct __tagVARIANT { 
      VARTYPE vt; 
      WORD wReserved1; 
      WORD wReserved2; 
      WORD wReserved3; 
      union { 
       LONG   lVal;   /* VT_I4    */ 
       BYTE   bVal;   /* VT_UI1    */ 
       SHORT   iVal;   /* VT_I2    */ 
       FLOAT   fltVal;  /* VT_R4    */ 
       DOUBLE  dblVal;  /* VT_R8    */ 
       VARIANT_BOOL boolVal;  /* VT_BOOL    */ 
       _VARIANT_BOOL bool;   /* (obsolete)   */ 
       SCODE   scode;  /* VT_ERROR    */ 
       CY   cyVal;  /* VT_CY    */ 
       DATE   date;   /* VT_DATE    */ 
       BSTR   bstrVal;  /* VT_BSTR    */ 
       IUnknown * punkVal;  /* VT_UNKNOWN   */ 
       IDispatch * pdispVal;  /* VT_DISPATCH   */ 
       SAFEARRAY * parray;  /* VT_ARRAY    */ 
       BYTE *  pbVal;  /* VT_BYREF|VT_UI1  */ 
       SHORT *  piVal;  /* VT_BYREF|VT_I2  */ 
       LONG *  plVal;  /* VT_BYREF|VT_I4  */ 
       FLOAT *  pfltVal;  /* VT_BYREF|VT_R4  */ 
       DOUBLE *  pdblVal;  /* VT_BYREF|VT_R8  */ 
       VARIANT_BOOL *pboolVal;  /* VT_BYREF|VT_BOOL  */ 
       SCODE *  pscode;  /* VT_BYREF|VT_ERROR */ 
       CY *   pcyVal;  /* VT_BYREF|VT_CY  */ 
       DATE *  pdate;  /* VT_BYREF|VT_DATE  */ 
       BSTR *  pbstrVal;  /* VT_BYREF|VT_BSTR  */ 
       IUnknown ** ppunkVal;  /* VT_BYREF|VT_UNKNOWN */ 
       IDispatch ** ppdispVal; /* VT_BYREF|VT_DISPATCH */ 
       SAFEARRAY ** pparray;  /* VT_BYREF|VT_ARRAY */ 
       VARIANT *  pvarVal;  /* VT_BYREF|VT_VARIANT */ 
       PVOID   byref;  /* Generic ByRef  */ 
       CHAR   cVal;   /* VT_I1    */ 
       USHORT  uiVal;  /* VT_UI2    */ 
       ULONG   ulVal;  /* VT_UI4    */ 
       INT   intVal;  /* VT_INT    */ 
       UINT   uintVal;  /* VT_UINT    */ 
       DECIMAL *  pdecVal;  /* VT_BYREF|VT_DECIMAL */ 
       CHAR *  pcVal;  /* VT_BYREF|VT_I1  */ 
       USHORT *  puiVal;  /* VT_BYREF|VT_UI2  */ 
       ULONG *  pulVal;  /* VT_BYREF|VT_UI4  */ 
       INT *   pintVal;  /* VT_BYREF|VT_INT  */ 
       UINT *  puintVal;  /* VT_BYREF|VT_UINT  */ 
      } __VARIANT_NAME_3; 
     } __VARIANT_NAME_2; 
     DECIMAL decVal; 
    } __VARIANT_NAME_1; 
}; 

Это определение варианта автоматизации OLE тип данных. Как вы видите, у него много возможных типов. Существует множество правил вокруг типов, которые вы можете использовать в разных ситуациях, в зависимости от возможностей вашего предполагаемого кода клиента. Не все типы поддерживаются всеми языками.

Типы с VT_BYREF после них используются такими языками, как VBScript, которые по умолчанию передают параметры по ссылке. Это означает, что если у вас есть код, который заботится о деталях структуры вариантов (например, C++), вызываемых кодом, который не работает (например, VB), тогда вам необходимо тщательно разыграть параметр варианта, если это необходимо.

Типы byref также используются для возврата значений из функций. Существует также поддержка типов массивов с использованием странно неверно названного типа SAFEARRAY - так сложно использовать из C++.

Если у вас есть массив строк, вы можете передать его в vbscript, но его нельзя использовать (кроме того, чтобы печатать размер). Чтобы действительно прочитать значения, данные массива должны быть типа VT_BYREF | VT_BSTR.

+31

OMG, у меня в глазах кровотечение! – paxdiablo

+0

Где объяснение? :-) –

+0

Я думал, что это объяснительно. Это определение типа данных варианта автоматизации OLE. Как вы видите, у него много возможных типов. Не все типы поддерживаются всеми языками –

2

Кстати, я просто использовал один из ответов here в Stackoverflow, чтобы обработать слово, состоящее из 6-битовых полей, как два 16-битных целых числа без знака.

Несколько лет назад я также использовал один для (первого) ARM C-компилятора - инструкции в те дни были все 32 бит, но имели разные макеты в зависимости от точной инструкции. Таким образом, у меня был союз для представления инструкции ARM, содержащей набор структур, каждая из которых имела соответствующие битовые поля для определенного типа команды.

+0

вещь ARM выглядит красивой и уродливой :) – claf

+0

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

3
struct InputEvent 
{ 
    enum EventType 
    { 
     EventKeyPressed, 
     EventKeyPressRepeated, 
     EventKeyReleased, 
     EventMousePressed, 
     EventMouseMoved, 
     EventMouseReleased 
    } Type; 
    union 
    { 
     unsigned int KeyCode; 
     struct 
     { 
      int x; 
      int y; 
      unsigned int ButtonCode; 
     }; 
    }; 
}; 
... 
std::vector<InputEvent> InputQueue; 

с профсоюзным взломом Я могу просто создать вектор объектов. Я уверен, что это можно сделать более чистым ...но это работает для меня - KISS

+0

SDL использует аналогичную структуру событий/объединение. – aib

6

Пожалуйста, избегайте «взломов» с помощью соединения, они вызывают головные боли переносимости (сущность, проблемы с выравниванием).

  • Законное использование объединения для хранения различных типов данных в одном месте, желательно с тэгом, так что вы знаете, какой тип он. См. Пример по 1800 INFORMATION.

  • Не используйте соединение для преобразования между типами данных, например. от целого числа до нескольких байтов. Вместо переносимости используйте смену и маскировку.

+0

Какой-то союз (см. Pthread.h на linux) помогает переносить, не так ли? – claf

+1

В моей системе нет объединений в файлах pthread.h (все 48 из них). – starblue

+0

Посмотрите на источник nptl в glibc – claf

1
#define DWORD unsigned int 
#define WORD unsigned short 
#define BYTE unsigned char 

typedef union _DWORD_PART_ { 

    DWORD dwWord; 

    struct { 
     WORD dwMSB; 
     WORD dwLSB; 
    }hw; 

    struct { 

     BYTE byMSB; 
     BYTE byMSBL; 
     BYTE byLSBH; 
     BYTE byLSB; 

    } b; 

} DWORD_PART; 

Это простой способ получить доступ к словам частей. (После того, как вы закончите, любое изменение в допуске платформы также может быть легко обработано)

+1

Обратите внимание, что это зависит от поведения, определенного реализацией. Он переносится только на платформы, где реализация определяет, что она будет иметь семантику, которую вы намереваетесь. Право на платформу не перекрывать хранение членов профсоюза, например, до тех пор, пока оно задокументировано. – RBerteig

+1

Я не понимаю, когда вы говорите, что это не сработает. Он работает в Windows, Linux, ARM, MacOS и PPC. Не раз я видел, как он терпит неудачу. Во всяком случае, я думал, что это может быть полезно, если вы не хотите, чтобы SU. – Alphaneo

+2

-1 Это неправильно для небольших конечных машин (например, x86). Пожалуйста, не используйте union для преобразования между машинными словами и байтами, это плохо для переносимости. – starblue

8

Союзы также широко используются на лексическом анализе и этапе синтаксического анализа языковых процессоров, таких как компиляторы и интерпретаторы. Вот я сейчас редактирую.

union { 
    char c; 
    int i; 
    string *s; 
    double d; 
    Expression *e; 
    ExpressionList *el; 
    fpos_t fp; 
} 

Объединение используется для связывания семантических значений с маркерами лексического анализатора и производств парсера. Эта практика довольно распространена в генераторах грамматики, таких как yacc, что обеспечивает явную поддержку для нее. Союз может хранить любые его значения, но только один из них в то время. Например, в любой точке из входного файла вы либо читаете константу символа (хранящуюся в c), либо целое число (хранящееся в i) или число с плавающей запятой (хранящееся в d). Генератор грамматики обеспечивает значительную помощь для определения того, какое из значений сохраняется в любой момент времени в зависимости от обрабатываемого правила.

3

Мы используем союзы для упакованных сообщений на работе (C/C++), поэтому мы можем передавать структуру с объединением в качестве члена данных, а затем обращаться к правильному пути на основе поля id в структуре.

Работали находка, пока кто-нибудь не написал структуру в файл, теперь мы ограничены крупнейших данных, используемых в файле, потому что даже думал, что это версия файла, никто не изменил его ....

Так что пока полезно для работы в памяти, избегайте слепо записывать их на диск или в сеть.