Объединения
Объединение представляет собой пользовательский тип, составленный из полей, расположенных в одной и той же области памяти, за счет чего они накладываются друг на друга. Это дает возможность записать в объединение некое значение одного типа, а затем прочитать его внутреннее представление (на уровне битов) в интерпретации для другого типа. Таким образом можно обеспечить нестандартную конвертацию из одного типа в другой.
Поля объединения могут быть любых встроенных типов, за исключением строк, динамических массивов и указателей. Также в объединениях можно использовать структуры с такими же простыми типами полей и без конструкторов/деструкторов.
Компилятор выделяет под объединение ячейку памяти размером, равным максимальному размеру среди типов всех элементов. Так, для объединения с полями типа long (8 байтов) и int (4 байта) будет выделено 8 байтов.
Все поля объединения расположены по одному и тому же адресу памяти, то есть они выровнены по началу объединения (имеют смещение 0, что можно проверить с помощью offsetof, см. раздел Упаковка структур).
Синтаксис описания объединения аналогичен структуре, но использует ключевое слово union. За ним идет идентификатор и далее блок кода с перечнем полей.
Например, в алгоритме может использоваться массив типа double для хранения различных настроек, просто потому что тип double один из числа тех, что имеют максимальный размер в байтах: 8. Допустим, среди настроек есть числа типа ulong. Поскольку тип double не гарантирует точное воспроизведение больших значений ulong, требуется воспользоваться объединением для "упаковки" ulong в double и "распаковки" обратно.
#define MAX_LONG_IN_DOUBLE 9007199254740992
|
Размер структуры ulong2double равен 8, поскольку оба его поля имеют этот размер. Таким образом, поля U и D полностью перекрываются.
В области целых чисел значение 9007199254740992 является наибольшим, для которого гарантированно устойчивое хранение в double. В данном примере мы пытаемся сохранить в double на единицу большее число.
Стандартная конвертация из ulong в double приводит к потере точности: после записи 9007199254740993 в переменную d типа double мы читаем из неё уже "округленное" значение 9007199254740992 (о тонкостях хранения чисел в типе double см. раздел Вещественные числа).
При использовании конвертера число 9007199254740993 записывается в объединение "как есть", без конвертаций, поскольку мы присваиваем его полю U типа ulong. Его представление с точки зрения double доступно, опять-таки без конвертаций, из поля D. Мы можем его копировать в другие переменные и массивы типа double без опасений.
Хотя полученное значение double выглядит странно, оно в точности соответствует исходному целому, если его потребуется извлечь путем обратной конвертации: записываем в поле D типа double, потом читаем из поля U типа ulong.
Объединение может иметь конструкторы и деструкторы, а также методы. По умолчанию члены объединения имеют публичные права доступа, но это можно откорректировать с помощью модификаторов доступа, как в структуре.