Зачем нам нужны Союзы C? - PullRequest
217 голосов
/ 31 октября 2008

Когда следует использовать профсоюзы? Зачем они нам нужны?

Ответы [ 18 ]

3 голосов
/ 27 сентября 2013

Многие из этих ответов касаются приведения из одного типа в другой. Я получаю наибольшую пользу от союзов с одними и теми же типами, только больше (например, при разборе последовательного потока данных). Они позволяют разбору / построению пакета в рамке стать тривиальными.

typedef union
{
    UINT8 buffer[PACKET_SIZE]; // Where the packet size is large enough for
                               // the entire set of fields (including the payload)

    struct
    {
        UINT8 size;
        UINT8 cmd;
        UINT8 payload[PAYLOAD_SIZE];
        UINT8 crc;
    } fields;

}PACKET_T;

// This should be called every time a new byte of data is ready 
// and point to the packet's buffer:
// packet_builder(packet.buffer, new_data);

void packet_builder(UINT8* buffer, UINT8 data)
{
    static UINT8 received_bytes = 0;

    // All range checking etc removed for brevity

    buffer[received_bytes] = data;
    received_bytes++;

    // Using the struc only way adds lots of logic that relates "byte 0" to size
    // "byte 1" to cmd, etc...
}

void packet_handler(PACKET_T* packet)
{
    // Process the fields in a readable manner
    if(packet->fields.size > TOO_BIG)
    {
        // handle error...
    }

    if(packet->fields.cmd == CMD_X)
    {
        // do stuff..
    }
}

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

1 голос
/ 29 августа 2011

В школе я использовал такие союзы:

typedef union
{
  unsigned char color[4];
  int       new_color;
}       u_color;

Я использовал его для более удобной обработки цветов, вместо использования операторов >> и <<, мне просто нужно было просмотреть другой индекс моего массива char.

1 голос
/ 29 августа 2011

Я использовал union, когда кодировал встроенные устройства. У меня есть C int, который 16-битный. И мне нужно извлечь старшие 8 бит и младшие 8 бит, когда мне нужно прочитать из / store в EEPROM. Поэтому я использовал этот способ:

union data {
    int data;
    struct {
        unsigned char higher;
        unsigned char lower;
    } parts;
};

Не требует сдвига, поэтому код легче читать.

С другой стороны, я видел какой-то старый код stl на C ++, который использовал union для распределителя stl. Если вам интересно, вы можете прочитать исходный код sgi stl . Вот часть этого:

union _Obj {
    union _Obj* _M_free_list_link;
    char _M_client_data[1];    /* The client sees this.        */
};
1 голос
/ 29 августа 2011

Как насчет VARIANT, который используется в интерфейсах COM? Он имеет два поля - «тип» и объединение, содержащее фактическое значение, которое обрабатывается в зависимости от поля «тип».

1 голос
/ 31 октября 2008

Союзы великолепны. Одно умное использование союзов, которые я видел, состоит в том, чтобы использовать их при определении события. Например, вы можете решить, что событие 32-битное.

Теперь в этих 32 битах вы можете указать первые 8 бит в качестве идентификатора отправителя события ... Иногда вы имеете дело с событием в целом, иногда вы анализируете его и сравниваете его компоненты , профсоюзы дают вам гибкость в обоих случаях.

union Event
{
  unsigned long eventCode;
  unsigned char eventParts[4];
};
1 голос
/ 20 апреля 2017

В ранних версиях C все объявления структуры имели общий набор полей. Дано:

struct x {int x_mode; int q; float x_f};
struct y {int y_mode; int q; int y_l};
struct z {int z_mode; char name[20];};

компилятор, по сути, будет создавать таблицу размеров структур (и, возможно, выравнивания), а также отдельную таблицу имен, типов и смещений элементов структур. Компилятор не отслеживал, какие элементы принадлежат каким структурам, и позволял бы двум структурам иметь элемент с одинаковым именем, только если тип и смещение совпадают (как с элементом q из struct x и struct y ). Если бы p был указателем на какой-либо тип структуры, p-> q добавило бы смещение «q» к указателю p и извлекло бы «int» из полученного адреса.

Учитывая вышеупомянутую семантику, можно было написать функцию, которая могла бы выполнять некоторые полезные операции над несколькими типами структур взаимозаменяемо при условии, что все поля, используемые функцией, выровнены с полезными полями в рассматриваемых структурах. Это была полезная функция, и изменение C для проверки элементов, используемых для доступа к структуре, по типам рассматриваемых структур означало бы потерю его в отсутствие средства, имеющего структуру, которая может содержать несколько именованных полей по одному и тому же адресу. Добавление типов «объединение» в C помогло несколько заполнить этот пробел (хотя, не так, IMHO, как следовало бы).

Существенной частью способности профсоюзов заполнить этот пробел является тот факт, что указатель на член объединения может быть преобразован в указатель на любое объединение, содержащее этот член, а указатель на любой объединение может быть преобразован в указатель на любой участник. В то время как Стандарт C89 прямо не сказал, что приведение T* непосредственно к U* было эквивалентно приведению его к указателю на любой тип объединения, содержащему как 1013 *, так и U, а затем приведение к U*, определенное поведение последней последовательности приведения не будет затронуто используемым типом объединения, и в стандарте не указана какая-либо противоположная семантика для прямого приведения от T до U. Кроме того, в случаях, когда функция получает указатель неизвестного происхождения, поведение записи объекта с помощью T*, преобразования T* в U* и последующего чтения объекта с помощью U* будет эквивалентно записи объединение через член типа T и чтение как тип U, что в некоторых случаях будет определено стандартом (например, при доступе к элементам Common Initial Sequence) и определено реализацией (а не неопределено) для остальных. В то время как программы редко использовали гарантии CIS с реальными объектами типа объединения, гораздо чаще использовался тот факт, что указатели на объекты неизвестного происхождения должны были вести себя как указатели на членов объединения и иметь связанные с этим поведенческие гарантии.

1 голос
/ 29 августа 2011
  • Файл, содержащий записи разных типов.
  • Сетевой интерфейс, содержащий различные типы запросов.

Взгляните на это: Обработка команды буфера X.25

Одна из многих возможных команд X.25 принимается в буфер и обрабатывается на месте с использованием UNION всех возможных структур.

0 голосов
/ 18 сентября 2013

Простой и очень полезный пример: ....

Представьте себе:

у вас есть uint32_t array[2] и вы хотите получить доступ к третьему и четвертому байту цепочки байтов. Вы могли бы сделать *((uint16_t*) &array[1]). Но это, к сожалению, нарушает строгие правила псевдонимов!

Но известные компиляторы позволяют вам делать следующее:

union un
{
    uint16_t array16[4];
    uint32_t array32[2];
}

технически это все еще является нарушением правил. но все известные стандарты поддерживают это использование.

...