Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
КРАТКИЙ ОБЗОР С.doc
Скачиваний:
1
Добавлен:
26.10.2018
Размер:
2.11 Mб
Скачать

2.6.2. Объединения

      Рассмотрим таблицу имен, в которой каждый элемент содержит имя и       его значение. Значение может задаваться либо строкой, либо целым числом:       struct entry {       char* name;       char type;       char* string_value; // используется если type == 's'       int int_value; // используется если type == 'i'       };       void print_entry(entry* p)       {       switch(p->type) {       case 's':       cout << p->string_value;       break;       case 'i':       cout << p->int_value;       break;       default:       cerr << "type corrupted\n";       break;       }       }       Поскольку переменные       string_value и int_value никогда не могут использоваться одновременно,       очевидно, что часть памяти пропадает впустую. Это можно легко исправить,       описав обе переменные как члены объединения, например, так:       struct entry {       char* name;       char type;       union {       char* string_value; // используется если type == 's'       int int_value; // используется если type == 'i'       };       }; Теперь гарантируется, что при выделении памяти для entry члены string_value и int_value будут размещаться с одного адреса, и при этом не нужно менять все части программы, работающие с entry. Из этого следует, что все члены объединения вместе занимают такой же объем памяти, какой занимает наибольший член объединения.       Надежный способ работы с объединением заключается в том, чтобы выбирать значение с помощью того же самого члена, который его записывал. Однако, в больших программах трудно гарантировать, что объединение используется только таким способом, а в результате использования не того члена обЪединения могут возникать трудно обнаруживаемые ошибки. Но можно встроить объединение в такую структуру, которая обеспечит правильную связь между значением поля типа и текущим типом члена объединения ($$5.4.6).       Иногда объединения используют для "псевдопреобразований" типа (в основном на это идут программисты, привыкшие к языкам, в которых нет средств преобразования типов, и в результате приходится обманывать транслятор). Приведем пример такого "преобразования" int в int* на машине VAX, которое достигается простым совпадением разрядов:       struct fudge {       union {       int i;       int* p;       };       };       fudge a;       a.i = 4095;       int* p = a.p; // некорректное использование В действительности это вовсе не преобразование типа, т.к. на одних машинах int и int* занимают разный объем памяти, а на других целое не может размещаться по адресу, задаваемому нечетным числом. Такое использование объединений не является переносимым, тогда как существует переносимый способ задания явного преобразования типа ($$3.2.5).       Иногда объединения используют специально, чтобы избежать преобразования типов. Например, можно использовать fudge, чтобы узнать, как представляется указатель 0:       fudge.p = 0;       int i = fudge.i; // i необязательно должно быть 0       Объединению можно дать имя, то есть можно сделать его полноправным типом. Например, fudge можно описать так:       union fudge {       int i;       int* p;       }; и использовать (некорректно) точно так же, как и раньше. Вместе с тем, поименованные объединения можно использовать и вполне корректным и оправданным способом (см. $$5.4.6).