46019 (665326), страница 12
Текст из файла (страница 12)
Память распределяется структуре покомпонентно, слева-направо, от младшего к старшему адресу памяти. В следующем примере
struct mystruct (*
int i;
char str[2];
double d;
*) s;
объект s занимает достаточное количество памяти для размещения 2- байтового целочисленного значения, 21-байтовой строки и 8-байтового значения типа double. Формат данного объекта в памяти определяется опцией Turbo C++ выравнивания по границе слова. Когда эта опция выключена (по умолчанию), s будет занимать 31 байт непрерывно. Если же включить выравнивание по границе слова опцией -a компилятора (или в диалоговом поле Options \! Compiler \! Code Generation), то Turbo C++ заполняет структуры байтами таким образом, что структура была выравнена по следующим правилам:
1. Структура должна начинаться по границе слова (четный адрес).
2. Любой не-символьный элемент будет иметь четное смещение в байтах относительно начала структуры.
3. В конец структуры при необходимости добавляется конечный байт, таким образом, чтобы вся структура в целом занимала четное количество байтов.
Если опция выравнивания включена, топриведенный пример структуры имел бы добавленный перед double байт, и весь объект в целом занимал бы 32 байта.
Пространство имен структур
Имена тегов структур разделяют общее пространство имен стегами объединений и перечислимых данных (однако в С++ имена входящих в структуру перечислимых данных находятсяв другом адресном пространстве). Это означает, что в пределах одного контекста такие теги должны иметь уникальные имена. Однако, имена тегов не обязаны отличаться от идентификаторов, находящихся в трех других адресных пространствах: пространстве имен меток, пространстве (пространствах) имен компонентов и едином адресном пространстве (которое состоит из имен переменных, функций, имен typedef и нкмераторов).
Имена компонентов в пределах данной структуры или объединения лбязаны быть уникальными, но среди разных структур или объединений они могут совпадать. Например,
goto s;
...
struct s (* // так можно; теги и имена меток находятся в разных // адресных пространствах
int s; // так можно; теги, имена меток и имена компонентов // дятся в разных адресных пространствах
float s;// так нельзя: повторение имени компонентов структур *) s; // так можно; пространства имен переменных различны // В С++ это допустимо только если s не имеет
// конструктора.
union s (* // так нельзя: повторение имен в пространстве тегов int s; // так можно: новое пространство компонентов float f;
*) f; // так можно: пространство имен переменных
struct t (*
int s; // так можно: следующее пространство имен компоненто
...
*) s; // так нельзя: повторение имен переменных
Неполные объявления
Указатель структуры типа А допустим в объявлении другой структуры В до объявления структуры А:
struct A;// неполное объявление struct B (* struct A *pa *);
struct A (* struct B *pb *);
Первое объявление А называется неполным, поскольку в этой точке отсутствует определение А. В данной ситуации неполное объявление допустимо, поскольку в объявлении В размер А необязателен. Битовые поля
Структура можетсодержать любые комбинации битовых полей с данными других типов.
Целочисленные компоненты типа signed или unsigned можно объявить битовыми полями шириной от 1 до 16 бит. Ширина битового поля и его опциональный идентификатор задаются следующим образом:
спецификатор-типа :ширина;
где спецификатор-типа это char, unsigned char, int или unsigned int. Битовые поля располагаются с нижнего и кончая саршим битом слова. Выражение "ширина" должно быть задано и должно давать целочисленную константу со значением в диапазоне от 0 до 16.
Если идентификатор битового поля опущен, то число битов, заданное выражением "ширина", распределяется в памяти, но поле при этом остается недоступным программе. Это позволяет создавать битовые шаблоны для, например, аппаратных регистров компьютера, в которых некоторые биты не используются. Пример:
struct mystruct (*
int i:2;
unsigned j:5;
int :4;
int k:1;
unsigned m:4;
*) a, b, c;
создает следующее распределение памяти.
\! | \! 15\! 4 \! 3 \! | 14\! 13\! 12\! 11\! 2 \! 1 \! 0 \! | 10\! | 9 | \ | 8 | \! | 7 | \! | 6 | \! | 5 |
\! | \! x \! x \! x \! | x \! x \! x \! x \! x \! x \! x \! | x \! | x | \ | x | \! | x | \! | x | \! | x |
-- | \!<---- ->\!<---- | -------->\!\!<--- >\! | ---- | - | - | >\ | <- | - | -- | - | -- | - |
\ | m \! k \ | не используется\! | j | \ | i | \! |
Целочисленные поля хранятся в виде дополнения до двух, причем крайний левый бит побещается в MSB (наиболее значащий бит).Для битового поля типа Int (например, signed) MSB интерпретируется как знаковый бит. Битовое поле шириной 2, содержащее двоичное 11, будет, следовательно, в случае типа unsigned интерпретироватьсякак 3, а в случае Int как -1. В предыдущем примере допустимое выражение a.i = 6 поместит в a.i двоичное 10 = -2, не выдавая каких-либо предупреждений. Поле k типа signed int шириной 1 может содержать только значения -1 и 0, так как битовый шаблон 1 будет интерпретирован как -1.
Примечание
Битовые поля могут быть объявлены только в структурах, объединениях и классах. Доступ к ним выполняется теми жеселекторами компонентов (. и ->), что используются для доступа к компонентам других типов. Кроме того, битовые поля вызывают некоторые проблемы с созданием переносимых кодов, поскольку организация битов в байтах и байтов в словах зависит от конкретной машины.
Выражение &mystruct.x недопустимо, так как x это идентификатор битового поля, а никакой гарантии, что mystruct.x имеет адрес на границе байта, нет.
Объединения
Объединения соответствуют типам вариантных записей языков Pascal и Modula-2.
Типы объединений являются производными типами, разделяющими многие синтаксические и функциональные свойства типов структур. Главное отличие между ними состоит в том, что объединение позволяет быть "активным" одновременно только одному компоненту. Размер объединения равен таким образом размеру своего максимального компонента. Одновременно в памяти может находиться значение только одного компонента лобъединения. В следующем простом случае
union myunion (* /* тег объединения = myunion */
int i;
double d;
char ch;
*) mu, *muptr=μ
идентификатор mu типа union myunion может служить для хранения 2-байтового значения int, 8-байтового значения double или 1-байтового char, но одновременно - только одного из этих значений.
Обе операции sizeof(union myunion) и sizeof (mu) возвращают значение 8, но когда mu содержит объект типа int, то 6 байт остаются неиспользованными (туда помещаются символы-заполнители), а когда mu сщдержит объект типа char - то 7 байт. Доступ к компонентам объединения выполняетсяпри помощи селекторов компонента структуры (. и ->), но требуется соблюдать осторожность:
mu.d = 4.016;
printf("mu.d = %f\n",mu.d);// порядок: на дисплее mu.d = 4.016
printf("mu.i = %f\n",mu.i);// забавный результат ! mu.ср = 'A';
printf("mu.ch = %c\n",mu.ch); // порядок: на дисплее mu.ch = A
printf("mu.d = %f\n",mu.d); // забавный результат ! muptr->i = 3; printf("mu.i = %d\n",mu.i); // порядок: на дисплее mu.i = 3
Второй оператор printf допустим, поскольку mu.i целочисленного типа. Однако, битовая комбинация в mu.i соответствует части ранее присвоенного значения типа double и не даст как правило полезной целочисленной интерпретации.
При правильных преобразованиях указатель объединения может указывать на любые его компоненты, и наоборот.
Объявления объединений
Общий синтаксисобъявления объединений во многом напоминает синтаксис объявления структур. Различия состоят в следующем:
1. Объединения могут содержать битовые поля, но активным бывает только одно изних. Все они начинаются в начале объединения.
2. С++ : В отличие от структур С++, объединения С++ не могут использовать спецификаторы класса доступа:public, private и protected. Все поля объединения имеют доступ private.
3. Объединения инициализируются через компонент, объявленный первым
union local87 (*
int i;
double d;
*) a = (* 20*);
4. С++ : Объединение не может участвовать в иерархии класса. Оно не может являться производным от какого-либо класса или быть базовым классом. Объединение может иметь конструктор.
5. С++ : Анонимные объединения не могут иметь компоненты-функции.
Перечислимые данные
Тип перечислимых данных служит для обеспечения мнемонических идентификаторов набора целочисленных значений. Например, следующее объявление:
enum days (* sun, mon, tues, wed, thur, fri, sat *) anyday;
устанавливает уникальный интегральный тип, enum days, переменную anyday этого типа и набор нумераторов (sun,mon,...), которым соответствуют целочисленные константы.
Turbo C++ может хранить нумераторы в одном байте, если это позволяет диапазон значений нумераторов, когда выключена опция -b (по умолчанию она включена; это означает, что данные типа enum всегда int), но при использовании их в выражениях выполняется этих данных преобразования к типу int. Идентификаторы, используемые в списке нумераторов, неявно получают тип unsigned char или int, в зависимости от значений нумераторов. Если все значения могут быть представлены типом unsigned char, то это и будет типом каждого нумератора.
C++ В С переменной перечислимого типа может быть присвоено любое значение типа int - кроме этого, никакого контроля типа не выполняется. В С++ переменной перечислимого типа может присваиваться только значение одного из ее нумераторов. Таким образом,
anyday = mon; // так можно
anyday = 1; // так нельзя, даже хотя mon == 1
Идентификатор days является опциональным тегом перечислимого типа,который можно использовать в последующих объявлениях переменных перечислимого типа enum days:
enum days payday, holiday; // объявление двух переменных
С++ В С++ ключевое слово enum можно опустить, если в пределах данного контекста имя days не дублируется.
Как и в случае объявлений struct и union, если далее переменные данного типа enum не требуются, тег может быть опущен:
enum (* sun, mon, tues, wed, thur, fri, sat *) anyday;
/* анонимный тип enum */
Подробное описание констант перечислимого типа см. на стр. 17 оригинала.
Нумераторы, перечисленные внутри фигурных скобок, называются перечислимыми константами. Каждой из них назначается фиксированное целочисленное значение. При отсутствии явно заданных инициализаторов первый нумератор (sun) устанавливается в ноль, а каждый последующий нумератор имеет значение на единицу больше, чем предыдущий (mon = 1, tue = 2 и т.д.).
При наличии явных интегральных инициализаторов вы можете установить один или более нумераторов в конкретные значения. Любые последующие имена без инициализаторов будут получать приращение в единицу. Например, в следующем объявлении
/* выражение инициализатора может включать в себя нумераторы, объявленные ранее */
enum coins (* penny = 1, tuppence, nickel = penny + 4, dime =10, quarter = nickel * nickel *) smallchange;
tuppence примет значение 2, nickel - значение 5, а quarter - значение 25.
Инициализатор может быть любым выражением, дающим положительное или отрицательное целочисленное значение (после, возможно, некоторых целочисленных преобразований.) Обычно такие значения бывают уникальными, но дублирование их также не запрещено.
Тип enum может участвовать во всех конструкциях,допускающих использование типов int.
enum days (* sun, mon, tues, wed, thur, fri, sat *) anyday;
enum days payday;