Союз - бесполезный анахронизм или полезная уловка старой школы?

Сравнение времени двух методов из 2.7 показывает, что они практически идентичны:

>>> setup_string = "a = sorted(dict({2:3, 1:89, 4:5, 3:0}).items())"
>>> timeit.timeit(stmt="[(k, val) for k, val in a]", setup=setup_string, number=10000)
0.003599141953657181

>>> setup_string = "from collections import OrderedDict\n"
>>> setup_string += "a = OrderedDict({1:89, 2:3, 3:0, 4:5})\n"
>>> setup_string += "b = a.items()"
>>> timeit.timeit(stmt="[(k, val) for k, val in b]", setup=setup_string, number=10000)
0.003581275490432745 
20
задан timrau 14 August 2012 в 23:50
поделиться

11 ответов

UNIONs implement some sort of polymorphism in a non-OOP world. Usually, you have a part which is common and depending on that part, you use the rest of the UNIONs. Therefore, in such cases where you do not have an OOP language and you want to avoid excessive pointer arithmetic, unions can be more elegant in some cases.

24
ответ дан 29 November 2019 в 22:31
поделиться

It's useful for setting bits in, say, registers instead of shift/mask operations:

typedef union {
    unsigned int as_int; // Assume this is 32-bits
    struct {
        unsigned int unused1 : 4;
        unsigned int foo : 4;
        unsigned int bar : 6;
        unsigned int unused2 : 2;
        unsigned int baz : 3;
        unsigned int unused3 : 1;
        unsigned int quux : 12;
    } field;
} some_reg;

Note: Which way the packing happens is machine-dependent.

some_reg reg;
reg.field.foo = 0xA;
reg.field.baz = 0x5;
write_some_register(some_address, reg.as_int);

I might have blown some syntax somewhere in there, my C is rusty :)

EDIT:

Incidentally, this works the opposite way also:

reg.as_int = read_some_register(some_address);
if(reg.field.bar == BAR_ERROR1) { ...
18
ответ дан 29 November 2019 в 22:31
поделиться

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

10
ответ дан 29 November 2019 в 22:31
поделиться

Ссылка Aonix выше хороша для основных рассуждений. Если вам нужны примеры того, как работают определенные стандарты, вы можете найти в Google следующее:

  • IEC61508: «Функциональная безопасность электрических / электронных / программируемых электронных систем, связанных с безопасностью (E / E / PES)». Базовый стандарт, на который есть ссылки в различных отраслевых стандартах. Эта страница IEC является хорошей отправной точкой. Часть 61508-3 посвящена программному обеспечению.
  • DO-178B: Стандарт авионики с той же сферой применения, что и IEC61508, который немного отличается от программного обеспечения
  • IEC60601-1-4:
6
ответ дан 29 November 2019 в 22:31
поделиться

Вы должны знать, что в C ++ они не такое уж хорошее решение, поскольку только типы POD (простые старые данные) могут быть объединены. Если ваш класс имеет конструктор, деструктор, содержит классы с конструкторами и / или деструкторами (и около миллиона других ошибок), он не может быть членом объединения.

8
ответ дан 29 November 2019 в 22:31
поделиться

Часто используется в спецификации протоколов передачи данных, когда вы хотите избежать потери места в структурах данных. Это позволяет экономить пространство памяти, используя одно и то же пространство для нескольких взаимоисключающих опций.

Например:

enum PacketType {Connect, Disconnect};
struct ConnectPacket {};
struct DisconnectPacket {};
struct Packet
{
    // ...
    // various common data
    // ...
    enum PacketType type;
    union
    {
        ConnectPacket connect;
        DisconnectPacket disconnect;
    } payload;
};

Структуры ConnectPacket и DisconnectPacket занимают одно и то же пространство, но это нормально, потому что пакет не может быть обоих типов в в то же время. Значение перечисления используется для определения того, какая часть объединения используется. Использование объединения позволило нам избежать дублирования общих частей структуры пакета.

5
ответ дан 29 November 2019 в 22:31
поделиться

Это довольно хороший способ получить значения битов IEEE для числа с плавающей запятой (конечно, при условии, что числа с плавающей запятой соответствуют стандарту IEEE в вашей системе). Все, что связано с приведением float * к int *, рискует нарушить строгие правила псевдонима. Это не только теоретически - высокий уровень оптимизации на самом деле сломает ваш код.

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

4
ответ дан 29 November 2019 в 22:31
поделиться

Рассмотрим случай доступа к отдельным байтам в большой переменной:

UInt32 x;
x = 0x12345678;
int byte_3 = x & 0x000000FF;          // 0x78
int byte_2 = (x & 0x0000FF00) >> 8;   // 0x56
int byte_1 = (x & 0x00FF0000) >> 16;  // 0x34
int byte_0 = (x & 0xFF000000) >> 24;  // 0x12

Это может быть намного элегантнее с объединением:

typedef union
{
    UInt32 value;  // 32 bits
    Byte byte[4];  // 4 * 8 bits
}
UInt32_Bytes;

UInt32_Bytes x;
x.value = 0x12345678;
int byte_3 = x.byte[3];  // 0x78
int byte_2 = x.byte[2];  // 0x56
int byte_1 = x.byte[1];  // 0x34
int byte_0 = x.byte[0];  // 0x12

Использование объединения означает, что вам больше не нужно использовать битовые маски и операторы сдвига для доступа к отдельным байтам. Он также делает явным доступ к байтам.

4
ответ дан 29 November 2019 в 22:31
поделиться

Я использовал его однажды для грубого полиморфизма данных, аналогично ответу markh44 . У меня было несколько разных типов данных, которые я хотел потенциально использовать. Я создал объединение всех этих типов и структуру, содержащую объединение и код, определяющий, какой тип должен использоваться.


union
{
    data_type_1;
    data_type_2;
    data_type_3;
} data_union;

typedef struct _TAG_DATA_WRAPPED_
{
    data_union data;
    int data_type; //better an enum
} WRAPPED_DATA;

WRAPPED_DATA loads_of_data[1024];


Чтобы ответить на ваш вопрос о том, почему это выгодно:

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

1
ответ дан 29 November 2019 в 22:31
поделиться

Мы Я использовал объединения во множестве кода для синтаксического анализа сетевых пакетов.

Union выделяет размер самого большого элемента. Вы должны создать объединение с элементом буфера максимального размера сообщения, тогда вы сможете легко получить доступ к значениям в пакете.

Представьте, что данные «c123456» поступили в оперативный режим, и вам нужно проанализировать и получить доступ к значениям:

  #include <iostream>
  using namespace std;

  struct msg
  {
     char header;
     union
     {
       char a[3];
       char b[2];
       char c[5];
       char d[6];
       char buf[10];
     } data;
  } msg;

  int main()
  {
    struct msg m;
    memcpy(&m, "c123456", sizeof("c123456"));

    cout << "m.header: " << m.header << endl;
    cout << "m.data.d: " << string(m.data.d,sizeof(m.data.d)) << endl;
    cout << "m.data.b: " << string(m.data.b,sizeof(m.data.b)) << endl;

    switch (m.header)
    {
     case 'a': cout << "a: " << string(m.data.a, sizeof(m.data.a)) << endl; break;
     case 'b': cout << "b: " << string(m.data.b, sizeof(m.data.b)) << endl; break;
     case 'c': cout << "c: " << string(m.data.c, sizeof(m.data.c)) << endl; break;
     default: break;
    }
  }

вывод будет выглядеть так:

m.header: c
m.data.d: 123456
m.data.b: 12
c: 12345
2
ответ дан 29 November 2019 в 22:31
поделиться

Я знаю, что это повторялось, но я просто опубликую образец кода, чтобы увидеть, как объединения добавляют элегантности и эффективности при чтении сетевого трафика:

#pragma packed(1)
struct header_t {
   uint16_t msg_id;
   uint16_t size;
};
struct command_t {
   uint8_t cmd;
};
struct position_t {
   uint32_t x;
   uint32_t y;
   uint32_t z;
};
// ... Rest of the messages in an IDS
struct message {
   header_t header;
   union {
      command_t command;
      position_t position;
   } body;
};
#pragma packed(0)
message read( int socket ) {
   message data;
   unsigned int readed = read( socket, &data, sizeof(header_t) );
   // error checks... readed bytes smaller than header size and such
   readed = read( socket, &(data.body), data.header.size ); 
   // error checks...
}

В приведенном выше фрагменте вы можете выполнить сообщение читается на месте, и вам не нужно заботиться о конкретном типе полученного объекта. Если вы не использовали объединение, вам осталось бы читать заголовок, извлекать размер и тип, создавать экземпляр объекта соответствующего типа (либо в иерархии, либо для включения в вариантный тип как boost :: any / boost :: variant) и выполнение второго чтения вновь созданного пространства.

Мы широко используем это решение для управления симуляторами (некоторые компании не ценят «новые» технологии, такие как DDS или HLA, и по-прежнему зависят от необработанного UDP / TCP данные для своих тренажеров). На сетевом уровне мы используем объединения, которые преобразуются во внутренние структуры данных (преобразование из сети в хост, масштабирование данных ...) перед подачей их на уровни приложений. Как упоминалось ранее, вы всегда должны быть осторожны с набивкой.

2
ответ дан 29 November 2019 в 22:31
поделиться
Другие вопросы по тегам:

Похожие вопросы: