Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
c++book1.doc
Скачиваний:
0
Добавлен:
01.05.2025
Размер:
2 Мб
Скачать
☆

2.5.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\n";

break;

*)

*)

Поскольку string_value и int_value никогда не могут ис­пользоваться одновременно, ясно, что пространство пропадает впустую. Это можно легко исправить, указав, что оба они долж­ны быть членами union. Например, так:

struct entry (*

char* name;

char type;

union (*

char* string_value; //используется если type == 's'

int int_value; //используется если type == 'i'

*);

*);

Это оставляет всю часть программы, использующую entry, без изменений, но обеспечивает, что при размещении entry string_value и int_value имеют один и тот же адрес. Отсюда следует, что все члены объединения вместе занимают лишь столько памяти, сколько занимает наибольший член.

Использование объединений таким образом, чтобы при чте­нии значения всегда применялся тот член, с применением кото­рого оно записывалось, совершенно оптимально. Но в больших программах непросто гарантировать, что объединения использу­ются только таким образом, и из-за неправильного использова­ния могут появляться трудно уловимые ошибки. Можно @капсули­зировать объединение таким образом, чтобы соответствие между полем типа и типами членов было гарантированно правильным (#5.4.6).

Объединения иногда используют для "объединения и преоб­разование типа" (это делают главным образом программисты, воспитанные на языках, не обладающих средствами преобразова­ния типов, где жульничество является необходимым). Например, это "преобразует" на VAX'е int в int*, просто предполагая по­битовую эквивалентность:

struct fudge (*

union (*

int i;

int* p;

*);

*);

fudge a;

a.i = 4096;

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.

2.6 Упражнения

1. (*1) Заставьте работать программу с "Hello, world" (1.1.1).

2. (*1) Для каждого описания в #2.1 сделайте следующее: Если описание не является определением, напишите для него оп­ределение. Если описание является определением, напишите для него описание, которое при этом не является определением.

3. (*1) Напишите описания для: указателя на символ; век­тора из 10 целых; ссылки на вектор из 10 целых; указателя на вектор из символьных строк; указателя на указатель на символ; константного целого; указателя на константное целое; и конс­тантного указателя на целое. Каждый из них инициализируйте.

4. (*1.5) Напишите программу, которая печатает размеры основных и указательных типов. Используйте операцию sizeof.

5. (*1.5) Напишите программу, которая печатает буквы 'a'...'z' и цифры '0'...'9' и их числовые значения. Сделайте то же для остальных печатаемых символов. Сделайте то же, но используя шестнадцатиричную запись.

6. (*1) Напечатайте набор битов, которым представляется указатель 0 на вашей системе. Подсказка: #2.5.2.

7. (*1.5) Напишите функцию, печатающую порядок и мантис­су параметра типа double.

8. (*2) Каковы наибольшие и наименьшие значения, на ва­шей системе, следующих типов: char, short, int, long, float, double, unsigned, char*, int* и void*? Имеются ли дополни­тельные ограничения на принимаемые ими значения? Может ли, например, int* принимать нечетное значение? Как выравниваются в памяти объекты этих типов? Может ли, например, int иметь нечетный адрес?

9. (*1) Какое самое длинное локальное имя можно исполь­зовать в С++ программе в вашей системе? Какое самое длинное внешнее имя можно использовать в С++ программе в вашей систе­ме? Есть ли какие-нибудь ограничения на символы, которые мож­но употреблять в имени?

10. (*2) Определите one следующим образом:

const one = 1;

Попытайтесь поменять значение one на 2. Определите num следующим образом:

const num[] = (* 1, 2 *);

Попытайтесь поменять значение num[1] на 2.

11. (*1) Напишите функцию, переставляющую два целых (ме­няющую значения). Используйте в качестве типа параметра int*. Напишите другую переставляющую функцию, использующую в ка­честве типа параметра int&.

12. (*1) Каков размер вектора str в следующем примере:

char str[] = "a short string";

Какова длина строки "a short string"?

13. (*1.5) Определите таблицу названий месяцев года и числа дней в них. Выведите ее. Сделайте это два раза: один раз используя вектор для названий и вектор для числа дней, и один раз используя вектор структур, в каждой из которых хра­нится название месяца и число дней в нем.

14. (*1) С помощью typedef определите типы: беззнаковый char, константный беззнаковый char, указатель на целое, ука­затель на указатель на char, указатель на вектора символов, вектор из 7 целых указателей, указатель на вектор из 7 целых указателей, и вектор из 8 векторов из 7 целых указателей.

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]