Зачем нужно объединение (union)?

1,00
р.
Например,
union { float fl, unsigned int uinteg, char ch, int integ } foo
Все это хранится вперемешку в одной области памяти. Какой в этом смысл, ведь однажды установив значения
foo.fl = 3.14f foo.uinteg = 666 foo.ch = 'a' foo.int = -25
уже не получится получить их назад - все перемешается? Способ сэкономить пару байтов или пару тактов и при этом сохранить читабельность? Не писать 4 разных функции, а написать одну, которая принимает union и в ней уже решать, что делать? В таком случае не проще ли принять void * и потом кастануть в тот тип, какой нужен? Как пример "Просто кастануть" приведу такой код:
Классический пример:
typedef enum { STR, INT } tType typedef struct { tType typ // typ is separate. union { int ival // ival and sval occupy same memory. char *sval } } tVal
void printer(tVal uni) { if (uni.type == INTEGER) // blah-blah uni.ival // Используем integer else ini.sval // В противном случае }
Функцию принтер можно переписать как-то так:
void printer(void* data, tType typ) { if (tType == INTEGER) { (int*)data // Чего-то делаем } }
Другой пример:
union { int a int b int c } bar
bar.a = 20 bar.b = 50 // Значение a потеряли :(
Опять-таки какой в этом смысл, если я могу сначала завести отдельную переменную int a = 20 а затем изменю ее значение a = 50 и эффект точно такой же? Выглядит как сильное колдовство.

Ответ
Union-ы (объединения) используют в двух случаях:
Для создания «универсального» типа данных, способного хранить не единственный, а один из предопределённых типов. Для этого к объединению добавляют целочисленное поле, указывающее тип хранимых в настоящий момент данных: struct variant { union tag_value { int intValue float floatValue } value unsigned storedType }
Один из примеров подобного применения в реальной жизни — структура VARIANT из Windows API. Иными словами, это предшественник современных boost::variant, QVariant и т. д. Однако вышеперечисленные классы могут хранить в себе непримитивные типы (с конструкторами, деструкторами и операторами копирования), а union — нет. Для преобразования между несовместимыми типами. Традиционно для этих целей используют оператор преобразования (T), либо reinterpret_cast<>. Однако эти способы опасны нарушением strict aliasing rule и, как результат, порождением неопределённого (то есть непредсказуемого) поведения. Правильные способы преобразования — это либо memcpy (подобный вызов которого выбрасывается компилятором), либо использование union-а. UPD: Внимание! Преобразование через union является допустимым только в Си, но не в C++. В ответе на вопрос «Accessing inactive union member and undefined behavior?» приводятся отсылки на следующие пункты стандартов:
c11
6.5.2.3 Структуры и члены объединений 95) Если поле, используемое для чтения содержимого объекта-объединения, не является полем, использованным ранее для записи значения в этот объект, требуемая часть внутреннего представления объекта интерпретируется в соответствием с представлением затребованного типа согласно 6.2.6 (данный процесс известен также как type punning). Это представление может приводить к неопределённому поведению.
6.5.2.3 Structure and union members 95) If the member used to read the contents of a union object is not the same as the member last used to store a value in the object, the appropriate part of the object representation of the value is reinterpreted as an object representation in the new type as described in 6.2.6 (a process sometimes called ‘‘type punning’’). This might be a trap representation.
c++11 (нет явного разрешения type punning-а)
9.5 Объединения [class.union] В объединении в каждый момент времени может быть активно только одно нестатическое поле вследствие этого в объединении в любой момент времени может находиться не более одного значения.
9.5 Unions [class.union] In a union, at most one of the non-static data members can be active at any time, that is, the value of at most one of the non-static data members can be stored in a union at any time.