Объединения часто используются для преобразования между двоичными представлениями целых чисел и плаваний:
union
{
int i;
float f;
} u;
// Convert floating-point bits to integer:
u.f = 3.14159f;
printf("As integer: %08x\n", u.i);
, Хотя это - технически неопределенное поведение согласно стандарту C (Вы, как только предполагается, читаете поле, которое было последний раз записано), это будет действовать четко определенным способом в фактически любом компиляторе.
Объединения также иногда используются для реализации псевдополиморфизма в C путем предоставления структуре некоторого тега, указывающего, какой объект это содержит, и затем unioning возможные типы вместе:
enum Type { INTS, FLOATS, DOUBLE };
struct S
{
Type s_type;
union
{
int s_ints[2];
float s_floats[2];
double s_double;
};
};
void do_something(struct S *s)
{
switch(s->s_type)
{
case INTS: // do something with s->s_ints
break;
case FLOATS: // do something with s->s_floats
break;
case DOUBLE: // do something with s->s_double
break;
}
}
Это позволяет размеру struct S
составлять только 12 байтов, вместо 28.
Объединения являются большими. Одно умное использование объединений, которые я видел, должно использовать их при определении события. Например, Вы могли бы решить, что событие составляет 32 бита.
Теперь, в тех 32 битах, Вы хотели бы определять первые 8 битов что касается идентификатора отправителя события... Иногда Вы имеете дело с событием в целом, иногда Вы разделяете его и выдерживаете сравнение, это - компоненты. объединения дают Вам гибкость, чтобы сделать обоих.
union Event { unsigned long eventCode; unsigned char eventParts[4]; };
Объединения используются, когда Вы хотите смоделировать структуры, определенные аппаратными средствами, устройствами или сетевыми протоколами, или когда Вы создаете большое количество объектов и хотите оставить свободное место. Вам действительно не нужны они 95% времени, хотя, придерживайтесь легкого к отладке кода.
Трудно думать об определенном случае при необходимости в этом типе гибкой структуры, возможно, в протоколе сообщения, куда Вы будете отправлять различные размеры сообщений, но даже тогда там, вероятно, лучше и больше программиста дружественные альтернативы.
Объединения немного похожи на различные типы на других языках - они могут только содержать одну вещь за один раз, но той вещью мог быть интервал, плавание и т.д. в зависимости от того, как Вы объявляете его.
, Например:
typedef union MyUnion MYUNION;
union MyUnion
{
int MyInt;
float MyFloat;
};
MyUnion будет только содержать интервал ИЛИ плавание, , в зависимости от которого Вы последний раз устанавливаете . Так выполнение этого:
MYUNION u;
u.MyInt = 10;
u теперь содержит интервал, равный 10;
u.MyFloat = 1.0;
u теперь содержит плавание, равное 1,0. Это больше не содержит интервал, Очевидно, теперь, при попытке сделать printf ("MyInt = % d", u. MyInt); тогда Вы, вероятно, собираетесь получить ошибку, хотя я не уверен в определенном поведении.
размер объединения диктует размер его самого большого поля, в этом случае плавание.
Вот пример объединения от моей собственной кодовой базы (из памяти и перефразируемый, таким образом, это не может быть точно). Это использовалось для хранения элементов языка в интерпретаторе, который я создал. Например, следующий код:
set a to b times 7.
состоит из следующих элементов языка:
, элементы Языка были, определяет как' #define
' значения таким образом:
#define ELEM_SYM_SET 0
#define ELEM_SYM_TO 1
#define ELEM_SYM_TIMES 2
#define ELEM_SYM_FULLSTOP 3
#define ELEM_VARIABLE 100
#define ELEM_CONSTANT 101
и следующая структура использовался для хранения каждого элемента:
typedef struct {
int typ;
union {
char *str;
int val;
}
} tElem;
тогда размер каждого элемента был размером максимального объединения (4 байта для типа и 4 байта для объединения, хотя те - типичные значения, фактический размеры cepend на реализации).
для создания элемента "набора", Вы использовали бы:
tElem e;
e.typ = ELEM_SYM_SET;
для создания "переменной [b]" элемент, Вы использовали бы:
tElem e;
e.typ = ELEM_VARIABLE;
e.str = strdup ("b"); // make sure you free this later
для создания "константы [7]" элемент, Вы использовали бы:
tElem e;
e.typ = ELEM_CONSTANT;
e.val = 7;
и Вы могли легко развернуть его для включения плаваний (float flt
) или rationals (struct ratnl {int num; int denom;}
) и другие типы.
основная предпосылка - то, что str
и val
не непрерывны в памяти, они на самом деле накладываются, таким образом, это - способ получить другое представление относительно того же блока памяти, проиллюстрированной здесь, где структура базируется в ячейке памяти 0x1010
и целые числа, и указатели - оба 4 байта:
+-----------+
0x1010 | |
0x1011 | typ |
0x1012 | |
0x1013 | |
+-----+-----+
0x1014 | | |
0x1015 | str | val |
0x1016 | | |
0x1017 | | |
+-----+-----+
, Если бы это было только в структуре, это было бы похоже на это:
+-------+
0x1010 | |
0x1011 | typ |
0x1012 | |
0x1013 | |
+-------+
0x1014 | |
0x1015 | str |
0x1016 | |
0x1017 | |
+-------+
0x1018 | |
0x1019 | val |
0x101A | |
0x101B | |
+-------+
Объединения позволяют элементы данных, которые являются взаимоисключающими для совместного использования той же памяти. Это довольно важно, когда память более недостаточна, такой как во встроенных системах.
В следующем примере:
union {
int a;
int b;
int c;
} myUnion;
Это объединение займет место единственного интервала, а не 3 отдельных международных значения. Если пользователь установил значение , и затем установил значение b, это перезаписало бы значение , так как они оба совместно используют ту же ячейку памяти.
Объединения особенно полезны во Встроенном программировании или в ситуациях, где прямой доступ к аппаратным средствам/памяти необходим. Вот тривиальный пример:
typedef union
{
struct {
unsigned char byte1;
unsigned char byte2;
unsigned char byte3;
unsigned char byte4;
} bytes;
unsigned int dword;
} HW_Register;
HW_Register reg;
Тогда можно получить доступ к reg следующим образом:
reg.dword = 0x12345678;
reg.bytes.byte3 = 4;
Порядок байтов (порядок байтов) и архитектура процессора, конечно, важен.
Другой полезной функцией является разрядный модификатор:
typedef union
{
struct {
unsigned char b1:1;
unsigned char b2:1;
unsigned char b3:1;
unsigned char b4:1;
unsigned char reserved:4;
} bits;
unsigned char byte;
} HW_RegisterB;
HW_RegisterB reg;
С этим кодом можно получить доступ непосредственно к единственному биту в регистре/адресе памяти:
x = reg.bits.b2;