Чтение онлайн

на главную

Жанры

Шрифт:

enum int16 (* sign=0100000, // знак most_significant=040000, // самый значимый least_significant=1 // наименее значимый *);

Такие значения не обязательно должны быть различными, возрастающими или положительными.

2.5 Экономия пространства

В ходе программирования нетривиальных разработок неизбежно наступает время, когда хочется иметь больше пространства памяти, чем имеется или отпущено. Есть два способа выжать побольше пространства из того, что доступно:

1. Помещение в байт более

одного небольшого объекта и

2. Использование одного и того же пространства для хранения разных объектов в разное время.

Первого можно достичь с помощью использования полей, второго – через использование объединений. Эти конструкции описываются в следующих разделах. Поскольку обычное их применение состоит чисто в оптимизации программы, и они в большинстве случаев непереносимы, программисту следует дважды подумать, прежде чем использовать их. Часто лучше изменить способ управления данными; например, больше полагаться на динамически выделяемую память (#3.2.6) и меньше на заранее выделенную статическую память.

2.5.1 Поля

Использование char для представления двоичной переменой, например, переключателя включено/выключено, может показаться экстравагантным, но char является наименьшим объектом, который в С++ может выделяться независимо. Можно, однако, сгруппировать несколько таких крошечных переменных вместе в виде полей struct. Член определяется как поле путем указания после его имени числа битов, которые он занимает. Допустимы неименованные поля; они не влияют на смысл именованных полей, но неким машинно-зависимым образом могут улучшить размещение:

struct sreg (* unsigned enable : 1; unsigned page : 3; unsigned : 1; // неиспользуемое unsigned mode : 2; unsigned : 4: // неиспользуемое unsigned access : 1; unsigned length : 1; unsigned non_resident : 1; *)

Получилось размещение регистра 0 состояния DEC PDP11/45 (в предположении, что поля в слове размещаются слева направо). Этот пример также иллюстрирует другое основное применение полей: именовать части внешне предписанного размещения. Поле должно быть целого типа и используется как другие целые, за исключением того, что невозможно взять адрес поля. В ядре операционной системы или в отладчике тип sreg можно было бы использовать так:

sreg* sr0 = (sreg*)0777572; //... if (sr-»access) (* // нарушение доступа // чистит массив sr-»access = 0; *)

Однако применение полей для упаковки нескольких переменных в один байт не обязательно экономит пространство. Оно экономит пространство, занимаемое данными, но объем кода, необходимого для манипуляции этими переменными, на большинстве машин возрастает. Известны программы, которые значительно сжимались, когда двоичные переменные преобразовывались из полей бит в символы! Кроме того, доступ к char или int обычно намного быстрее, чем доступ к полю. Поля – это просто удобная и краткая запись для применения логических операций с целью извлечения информации из части слова или введения информации в нее.

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).

Поделиться:
Популярные книги

Боги, пиво и дурак. Том 3

Горина Юлия Николаевна
3. Боги, пиво и дурак
Фантастика:
фэнтези
попаданцы
5.00
рейтинг книги
Боги, пиво и дурак. Том 3

Мымра!

Фад Диана
1. Мымрики
Любовные романы:
современные любовные романы
5.00
рейтинг книги
Мымра!

Белые погоны

Лисина Александра
3. Гибрид
Фантастика:
фэнтези
попаданцы
технофэнтези
аниме
5.00
рейтинг книги
Белые погоны

Любовь Носорога

Зайцева Мария
Любовные романы:
современные любовные романы
9.11
рейтинг книги
Любовь Носорога

Я все еще граф. Книга IX

Дрейк Сириус
9. Дорогой барон!
Фантастика:
боевая фантастика
попаданцы
аниме
5.00
рейтинг книги
Я все еще граф. Книга IX

Делегат

Астахов Евгений Евгеньевич
6. Сопряжение
Фантастика:
боевая фантастика
постапокалипсис
рпг
5.00
рейтинг книги
Делегат

Неудержимый. Книга XVI

Боярский Андрей
16. Неудержимый
Фантастика:
фэнтези
попаданцы
аниме
5.00
рейтинг книги
Неудержимый. Книга XVI

Чужая дочь

Зика Натаэль
Любовные романы:
любовно-фантастические романы
5.00
рейтинг книги
Чужая дочь

Ночь со зверем

Владимирова Анна
3. Оборотни-медведи
Любовные романы:
любовно-фантастические романы
5.25
рейтинг книги
Ночь со зверем

Месть бывшему. Замуж за босса

Россиус Анна
3. Власть. Страсть. Любовь
Любовные романы:
современные любовные романы
5.00
рейтинг книги
Месть бывшему. Замуж за босса

Совок – 3

Агарев Вадим
3. Совок
Фантастика:
фэнтези
детективная фантастика
попаданцы
7.92
рейтинг книги
Совок – 3

Титан империи 7

Артемов Александр Александрович
7. Титан Империи
Фантастика:
боевая фантастика
попаданцы
аниме
5.00
рейтинг книги
Титан империи 7

Измена. Он все еще любит!

Скай Рин
Любовные романы:
современные любовные романы
6.00
рейтинг книги
Измена. Он все еще любит!

Вечный Данж. Трилогия

Матисов Павел
Фантастика:
фэнтези
юмористическая фантастика
6.77
рейтинг книги
Вечный Данж. Трилогия