Технология объединений union берет свои истоки в 90-х. Слабенькие по нашим временам ЭВМ (сейчас их и компьютерами то не назовешь), мало памяти (все измерялось килобайтами). Жесткие диски по 40 мегабайт были чуть ли не чудом техники, олицетворяющим огроменные объемы информации, которые умельцами “растачивались” специальными ПО до хранения 80 мегабайт (чтоб побольше было).
Каждый байт памяти был на вес золота, приходилось экономить на всём. Вот и объединение переменных тоже было призвано (и достаточно успешно, о чем я расскажу ниже) помочь программисту сделать оптимальную и экономную программу, “кушающую” маленькие объемы памяти.
Чтобы понять в чем смысл объединения нужно вспомнить как хранятся переменные. Разные переменные разного типа или одинаковой группы типов (вроде int, long и short) несмотря на работу с одним и тем же типом данных (имею ввиду целое) занимают в памяти разное количество байт. long в любом случае занимает максимальное количество байт в памяти, при этом в память для переменной этого типа вполне можно записать значения int или short.
Просто получится, что не все зарезервированные байты long-а будут востребованы. Если поместить к примеру число 325 в long, будут заняты два байта (зависит от разрядности процессора), а остальные байты заполнятся нулями.
Именно в этом и появляется смысл union, ибо эта инструкция говорит компилятору: “Зарезервируй мне место для типа данных с максимальным запросом объема памяти, а я уже буду сама разбираться, как и какие значения в них положить”.
Объединение – это такой формат данных, который подобен структуре. Оно (объединение) способно хранить в пределах одной зарезервированной области памяти различные типы данных. Но в каждый определенный момент времени в объединении хранится только один из этих типов данных и возможно использовать лишь значение этого элемента (компонента). Синтаксически определяют объединение так (очень похоже на определение структуры):
1 2 3 4 5 6 | union { short int name1; int name2; long int name3; } myUnion; // объект объединения |
Доступ к элементам объединения осуществляется так же, как и к элементам структур: Имя объекта объединения myUnion , точка . и имя элемента name1 .
К данным, которые хранят элементы структуры (например short, int, long) мы можем обращаться в любой момент (хоть к одному, хоть и ко всем сразу). А в объединении могут храниться данные либо short, либо int, либо long. Например:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | #include <iostream> using namespace std; union { short int name1; int name2; long int name3; } myUnion; int main() { myUnion.name1 = 22; cout << myUnion.name1 << endl; myUnion.name3 = 222222222; cout << myUnion.name3 << endl; cout << myUnion.name1 << endl; // снова обращаемся к name1 } |
Запускаем программу:
Как видите, после того, как мы записали значение в элемент name3 типа long int , уже невозможно нормально обращаться к элементу name1 . Все потому, что в их общую память уже записано значение long int, а переменная типа short int неспособна работать с данными такого объема. Схематически можно это представить так:
Поэтому, чтобы опять работать с данными типа short int необходимо снова присвоить элементу name1 новое значение. Вот и получается – память одна и та же, а переменные в ней размещаются разные. К какой обращаемся – такая и запрашивает из этой памяти значение.
Элементы объединения располагаются в памяти начиная с одного места (как бы накладываются друг на друга), в отличии от элементов структуры (они располагаются в памяти последовательно один за другим).
Применение объединений вызвано необходимостью экономии памяти, когда нужно хранить и использовать данные разных типов, но обращаться к ним можно не одновременно.
Если бы мы описали просто набор переменных не объединяя их в union, то для их размещения потребовалось бы 2 + 4 + 4 байта = 10 байт. Вот и экономия. А так объединение занимает 4 байта. Целых 6 байт сэкономили, натрамбовав три переменные в один отрезок памяти.
К слову нужно заметить, что эта технология умудрилась получить неплохое продолжение, и в современных языках используется везде где нужно и не нужно. СиШарп, PHP, Делфи, современные наследники Си плюс плюс – все они используют такие объединения. Только в современном мире они уже называются variant.
В переменную variant можно без проблем запихнуть до 10 байт (10 это максимум для 32-х разрядных систем под вещественное число). Соответственно в эти 10 байт и все семейство целых и вещественных, и указатели, и объекты, и строки (точнее описатели строк или указатели на них) вмещаются.
На сегодняшний день экономия памяти не так актуальна, как 15-20 лет назад. Конечно, никто не запрещает использовать union, но зачем? Если есть более удобные на сегодня средства работы с памятью для программиста.
Следует отметить также, что программы для выполнения в память загружаются с избытком (т.е. для программы выделяется количество памяти, зачастую чрезвычайно большее чем надо). Это можно наблюдать, написав программу с одной переменной. Казалось бы – переменная 4 байта, но открыв диспетчер задач в Винде программа “почему-то” заняла 10 килобайт. Так работает современная операционная система, выделяя памяти с лихвой. В 90-х, увы, такой роскоши и быть не могло.
В следующем уроке мы рассмотрим битовые поля в С++. Не поленитесь посмотреть видео по теме Объединения (union) в С++:
Тебе реально бомбит?
Сначала не понял, потом прочитал ник этого индивидума, угарал 5 минут, спасибо!
Я так понял, что если мы в объединение на 8 байт (где будет дабл, допустим) засунем инт и шорт инт (на обоих нужно 6 байт), то он будет так же перезаписывать с первого байта, т.е. друг на друга?)
Я по объяснению так понял, но не понял почему не делится память под несколько переменных, если такая возможность есть