C++: Теоретические сведения: Структуры и объединения, перечисления
реклама
Структуры и объединения, перечисления
Структуры и объединения – это один из примеров составных типов данных, называемых агрегатными типами, или просто агрегатами. Структурные переменные, или просто структуры, – это объединение одной или более переменных, возможно, разных типов, в одну область памяти, имеющую одно имя. Отдельные составные части структурной переменной называются полями.
Объединения подобны структурам в том, что содержат поля различных типов, но помещаемые в одно и то же место памяти. Фактически объединения – это доступ к одному и тому же месту памяти, но по-разному.
Перечисления (enumeration) также задают особый тип данных, предназначенный для создания идентификаторов для целых констант.
Описание структур
Термин "структура" в языке программирования Си соответствует двум разных по смыслу понятиям:
1) обозначение места в памяти, где располагается информация; далее это место называется структурной переменной;
2) правила формирования структурной переменной, используемые компилятором для выделения ей места в памяти и организации доступа к ее полям; далее такие правила называются шаблоном (pattern) структуры или структурной переменной.
Как и любая переменная, структурная переменная должна описываться. Это описание состоит из двух шагов:
1) задание шаблона структуры;
2) собственно описание структурной переменной. Каждый шаблон имеет собственное имя для того, чтобы компилятор мог различать различные шаблоны. В том случае, если в функции используется единственный шаблон, он может не иметь имя, что задается пропуском имени при описании шаблона. Имена шаблонов должны быть уникальными в пределах их области определения, и в частности в пределах одной функции может быть только один безымянный шаблон. Синтаксис задания шаблона таков:
struct имя_шаблона {тип_1 имя_поля_1;
тип_2 имя_поля_2;
•••
тип_N имя_поля_N;};
где имя_шаблона – имя шаблона, удовлетворяющее правилам задания идентификаторов языка Си;
тип_1, тип_2,..., тип_N – любые типы, например int, char, float;
имя_поля_1, имя_поля_2,..., имя_поля_N – имена полей, удовлетворяющие правилам задания идентификаторов языка Си. Например:
struct BOOK { char name [20];
char title[44];
int year; float price; };
Имена полей в одном шаблоне должны быть уникальными. Однако в разных шаблонах можно использовать совпадающие имена полей. Кроме того, имена шаблонов проверяются на уникальность друг с другом, а не с метками или именами переменных. Раз так, то имя шаблона может и совпадать с именами полей, переменных или метками.
Задание шаблона не связано с резервированием какой-либо памяти компилятором. Шаблон дает компилятору всю необходимую информацию о полях структурной переменной для резервирования места в памяти и организации доступа к этой памяти при описании структурной переменной и ссылках на отдельные поля структурной переменной. Фактически шаблон – это задание нового типа struct имя_шаблона. В приведенном примере был введен новый тип struct BOOK.
Как и простая переменная, шаблон имеет область определения (видимость). Если шаблон описан внутри блока {}, – это локальный шаблон, видимый только из пределов данного блока, в частности из пределов конкретной функции. Если описание шаблона помещено вне блоков, такой шаблон видим во всех функциях ниже точки описания шаблона до границы файла. Нельзя описать шаблон с реквизитом extern.
Когда задан шаблон, может быть описана структурная переменная. Описание структурной переменной состоит из задания типа и имени структурной переменной. Например:
struct BOOK first_book;
Здесь описывается структурная переменная по приведенному ранее шаблону BOOK. Компилятор выделит под переменную число байтов памяти, достаточное для хранения всех ее полей. В данном случае это 70 байт. Число байтов, выделенное под структурную переменную, не всегда равно сумме длин отдельных полей из-за влияния дополнительного фактора внутреннего представления структурных переменных, называемого выравниванием. Точно выделенное число байтов всегда возвращает операция sizeof (struct имя_шаблона). Например, sizeof (struct BOOK) возвратит всегда 70, однако для других шаблонов возможны варианты.
Синтаксис языка программирования Си разрешает совмещать описание шаблона и структурной переменной, например так:
struct BOOK { char name[20];
char title[44];
int year; float price;
} first_book, child_book, dog_book;
Для структурных переменных действуют правила видимости и времени жизни.
Для доступа к отдельным полям структурной переменной используют операцию '.', формируя ссылку на нужное поле посредством имени структурной переменной и имени нужного поля. Такая ссылка может располагаться в любом месте выражений, где допустимы ссылки на простые переменные. Например, если сделано ранее рассмотренное описание структурных переменных, корректными будут следующие выражения:
first_book.year=1992;
dog_book.year=first_bookyear;
strcpy( child_book.title, "Белый Бим Черное Ухо");
scanf("%f', &first_book.price);
Ссылка на поле структурной переменной обладает всеми свойствами обычных переменных. Если поле – это массив символов, то например child_book.title – это указатель-константа на первый элемент массива. Можно определить адрес первого байта поля структурной переменной обычной операцией взятия адреса. Например, &first_book.price – это адрес первого байта поля. Естественно, что ссылка на поле структурной переменной может располагаться как слева, так и справа от операции присваивания. Так же, как и для обычных операндов, действуют правила преобразования типов при смешивании операндов разных типов.
Если структурные переменные соответствуют одному шаблону, допускается операция присваивания таких структурных переменных. Например, для ранее описанных структурных переменных first_book, child_book и dog_book будут корректными такие выражения:
first_book=chitd_book;
first_book=child_book=dog_book;
Операция присваивания структурных переменных приводит к физической пересылке в памяти числа байтов, равного размеру шаблона структурной переменной. В приведенном примере три структурные переменные будут иметь поля, совпадающие с полями структурной переменной dog_book.
При описании структурной переменной разрешается выполнять инициализацию полей переменной. Например:
реклама
struct BOOK { char name[20];
char title[44];
int year; float price; };
struct BOOK first_book=
{"Митчелл М",
"Унесенные ветром”,
1991. 5.25 },
child_book = { "Троепольскчй Г.Н.",
"Белый Бим Черное Ухо",
1980,2.78},
dog_book = {"Михайлов О.С",
"Воспитание щенка",
1968.0.47};
Разрешается объединять задание шаблона, описание структурных переменных и их инициализацию в одном предложении языка Си.
Для упрощения описания структурных переменных можно использовать оператор typedef описания собственного типа данных. Это упрощает текст программы и экономит силы на повторный набор слова struct при описании структурных переменных. Общая форма предложения конструирования собственного типа данных:
typedef описание_типа имя_нового_типа;
Например:
typedef struct BOOK { char name[20];
char title[44];
int year; float price;
} MY_BOOKS;
Затем можно использовать сконструированный тип для описания необходимых данных, например так:
MY_BOOKS dos_book; /* то же самое, что и struct BOOK, но запись несколько короче */
Здесь используется для создания собственного типа именованный шаблон BOOK.
Однако использование имени шаблона совсем не обязательно, т. е. можно записать:
typedef struct {char nате[20];
char title[44];
int year; float price;
}BOOKS;
typedef struct {char class, subclass;
int number;
} MY_UDC;
Вложение структур
Поле структурной переменной может иметь любой тип, в том числе и быть другой структурой. Поле, являющееся структурой, называют вложенной структурой. Естественно, что шаблон вкладываемой структуры должен быть уже известен компилятору. Например:
struct UDC {char class, subclass;
tnt number;};
struct BOOK{ struct UDC udk_clcss;
char name [20];
char title[44];
int year; float price; } first_book, child_book, dog_book;
В данном примере описывается шаблон структуры, имеющей вложенную структуру, и три структурные переменных по шаблону BOOK, т. е. и для вложенных структур действуют те же правила описания полей: задается шаблон (в данном примере это struct UDC) и имя поля (в шаблоне BOOK это udk_dass).
Ссылка на поле вложенной структуры формируется из имени структурной переменной, имени структурного поля и имени поля вложенной структуры. Перечисленные имена разделяются символом '.' операции точка. Например, если сделано описание структурных переменных по приведенному ранее шаблону BOOK с вложенной структурой UDK, будут корректны следующие выражения:
first_book.udc_class.class = 'А';
dog_book.udc_class.number = 681;
Единственное ограничение на вложение структур состоит в том, что структура не может вкладываться сама в себя, т. е. некорректным, например, будет следующее выражение:
struct BOOK { struct BOOK my_own; /* это ошибка */
char name [20];
char title[44];
int year; float price; };
Отметим, что разрешается использовать описываемый шаблон, если одно из полей является указателем на описываемую структуру. Например, будет корректным такое описание:
struct BOOK {
struct BOOK * my_own; /* это разрешается */
char name f 20J;
char title[44];
int year, floatprice; };
Указатель на структуру
Как уже отмечалось, описание шаблона структуры вводит по существу новый тип. Раз так, то ничего не мешает использовать указатель на введенный тип. Как и все указатели на данные, указатель на структурную переменную занимает либо два (near), либо четыре (far) байта. Для доступа к полям структурной переменной через указатель используется операция ->. Приведем пример описания указателя на структурную переменную, инициализации указателя и его использования для ссылки на отдельные поля структурной переменной:
struct UDC {char class, subclass;
int number; };
struct BOOK{ struct UDC udc_class;
char name[20];
char title[44];
int year; float price; };
struct BOOK * ptr_book, first_book;
ptr_book = &first_book;
ptr_book -> year = 1992;
ptr_book -> udc_class.class = 'A';
ptr_book -> udc_class.number = 681;
strcpy(ptr_book -> title, "Белый Бим Черное Ухо”);
scanf(“%f, & ptr_book -> price);
Ссылка на поле структурной переменной через указатель обладает всеми свойствами обычной переменной. Например, если поле – это массив, символов, то ptr_book -> title – это указатель-константа на первый элемент массива. Можно определить адрес первого байта поля структурной переменной обычной операцией взятия адреса. Например, &ptr_book -> price – это адрес первого байта поля. Естественно, что ссылка на поле структурной переменной через указатель может располагаться как слева, так и справа от знака операции присваивания. Вообще говоря, ссылка на поле через указатель – это синоним ссылки на это поле через операцию *. Например, если описан шаблон BOOK и сделано описание данных
реклама
struct BOOK* ptr_book,flrst_book;
ptr_book = &first_book:
то ссылка (*ptr).year эквивалентна ссылке
first_book.year и ссылке ptr-> year.
Заслуживает внимания то, как осуществляется доступ к полям вложенной структуры. Например, для того чтобы сослаться на поле class вложенной переменной по шаблону struct UDC, сначала используют операцию ->, а затем ссылку на поле вложенной структуры операцией '.':
ptr_book -> udc_class.class = 'A;
Используя указатель на структурную переменную, можно сделать ссылку на поле любого типа, в том числе, например, и на указатель. Отметим, что указатель на структуру может использоваться до объявления шаблона структуры, так как компилятору не требуется информация о размере структуры, а размер самого указателя известен. Далее приведен пример доступа к полям структуры с использованием указателя на структуру UDC, являющегося полем структуры BOOK:
struct BOOK{struct UDC * udc_class; /*указатель*/
char name [20];
char title [44];
int year; float price; };
struct UDC {char class, subclass;
int number; } my_class;
struct BOOK * ptr_book, first_book;
ptr_book = & first_book;
ptr_book -> udc_class = & my_class;
ptr_book -> year = 1992;
ptr_book -> udc_class -> class = 'A';
ptr_book -> udc_class -> number = 681;
strcpy( ptr book -> title, ”Белый Бum Черное Ухо” );
scanf("%f”, & ptr_book -> price);
В приведенном примере структурная переменная first_book, созданная по шаблону struct BOOK, включает поле udc_class. Оно представляет собой указатель на структурную переменную по шаблону struct UDC. Сам шаблон struct UDC описан после описания указателя udc_class. Операция присваивания
ptr_book -> udc_class = & my_class;
записывает в поле udc_class адрес первого байта структурной переменной my_class. После этого можно ссылаться на поля переменной udc_dass через указатель ptr_book, например, так:
ptr_book -> udc_class -> class = 'А';
Массивы структурных переменных
Указатели на структурные переменные широко используются для доступа к структурам, размещаемым в динамически выделяемой памяти, при создании сложных структур данных – списков, стеков, деревьев и пр., для передачи в качестве фактических аргументов функциям.
По заданному шаблону структуры можно описывать массивы структурных переменных. Описание такого массива ничем не отличается от описания массива обычных переменных. Так, например, выглядит описание массива из 25 элементов:
typedef struct {char name[20];
char title [44];
int year; float price; } BOOKS:
BOOKS library [25];
Доступ к элементам массива может выполняться с использованием индекса или через указатель-константу, которым является имя массива. Например, так можно выполнить доступ к полю year i-ro элемента:
library [i].year = 28; или
(*( library +i)).year=28; или
(library + i) -> year = 28;
А если описать рабочий указатель и инициализировать его адресом первого элемента массива
BOOKS library[25], *ptr= library;
то для доступа к полям i-ro элемента массива структурных переменных можно использовать указатель ptr:
(ptr+i)->year=28;
Продвижение указателя ptr операцией ++ или - - соответственно увеличивает или уменьшает его на размер типа BOOKS, т. е. устанавливает на следующий элемент массива структурных переменных вперед или назад. Это удобно при последовательном доступе к элементам массива.
Выделение оперативной памяти структурной переменной осуществляется по шаблону, интерпретируемому слева направо. При этом учитывается дополнительный фактор, называемый выравниванием структуры (Structure Alignement). Выравнивание задается либо как опция IDE (Options-Code Generation -Alignement), либо опцией компилятора командной строки. Может быть задано выравнивание структурной переменной на границе слова (Word Alignement в IDE, опция -а компилятора командной строки) или на границе байта (Byte Alignement в IDE, опция -а- компилятора командной строки; принимается по умолчанию).
При выравнивании структурной переменной на границе слова отдельные ее поля в памяти располагаются без "зазоров". Начинаться переменная будет с любого (четного или нечетного) адреса; ее длина равна сумме длин полей шаблона. При выравнивании на границе слова компилятор при размещении структурной переменной в памяти вставляет между ее полями и между элементами массива структурных переменных пустые байты для того, чтобы соблюдались следующие правила:
1) отдельная структурная переменная (элемент массива структурных переменных) начинается на границе слова, т. е. с четного адреса;
2) любое поле, тип которого не совпадает с типом char, будет начинаться с четного адреса (имеет четное смещение от начала структурной переменной);
3) при необходимости в конце структурной переменной добавляется еще один байт так, чтобы число байтов переменной было четным.
Выравнивание сказывается критическим образом при переносе структурных переменных между диском и памятью.
Передача структур функции
Как и для обычных переменных, С поддерживает как вызов функции с передачей копии всей структурной переменной или только отдельных ее полей (Call-By-Value), так и с передачей указателя на всю или только отдельные поля структурной переменной (Call-By-Reference). Функция может возвращать как структурную переменную, так и указатель на нее.
Вызов функции с передачей всей структурной переменной связан с дополнительными потерями времени на запись ее копии в стек. Зато при этом все манипуляции с копией не влияют на исходную переменную. По той же причине возврат функцией целой структурной переменной требует затрат на размещение в стеке возвращаемого значения.
Другая часто используемая возможность для передачи и (или) возврата структурных переменных – использование внешних структурных переменных, видимых как в точке вызова, так и в вызываемой функции.
Битовые поля в структурах
Язык Си допускает использование в структурах особого типа полей – так называемых битовых полей. Их использование делает возможным доступ к отдельным битам более крупных объектов, например байтов или слов. Общий синтаксис описания битового поля:
тип [имя ]: ширина;
где скобками [] выделены необязательные элементы.
Содержимое битового поля может описываться как имеющее знак (signed), так и как беззнаковое (unsigned). Два этих ключевых слова записываются в поле тип. Каждому полю выделяется точно столько бит, сколько задается в поле ширина. Ссылка на битовое поле выполняется по имени, указываемому в поле имя. Если имя в этом поле опущено, запрошенные биты все равно выделяются, но доступ к ним невозможен. Например:
struct EXAMPLE { int i:1;
unsigned j: 2;
int :3;
int k:2;
} my_struct;
При ссылке на поле в выражениях по маске выделяются нужные биты и при необходимости выполняется сдвиг числа вправо. В результате оно вступает в операцию в соответствии с типом как число со знаком или без него. При ссылке на битовое поле слева от операции присваивания выполняется обратная операция: сдвиг числа вправо, выделение по маске нужных битов и размещение их в структурной переменной поразрядной логической операцией с предыдущим содержимым поля.
Объединения
Как и для структур, различают шаблон объединения и переменную, созданную по шаблону. Именно ее далее будем называть объединением. Ключевым различием объединения и структурной переменной является то, что все поля объединения начинаются с одной границы. Другими словами, объединение – это способ по-разному обратиться к одной и той же области памяти. Длиной объединения является максимальная из длин отдельных полей шаблона. Тип поля может быть любым, в том числе и структурой. Доступ к полям, объединения выполняется через селектор поля '.', либо ссылка формируется операцией ->, если для доступа используется указатель на объединение. Разрешается создание и массива объединений. Объединение может инициализироваться только первым описанным полем.
Существует ряд весьма популярных применений объединений. Прежде всего – это доступ к одной и той же области памяти либо как к целому, либо к отдельным частям. Еще одно удобное применение объединения – формирование целого из частей, например, far-указателя по известным значениям смещения и сегмента.
Перечисления
Тип данных "перечисление" (enumeration) используется для облегчения создания мнемонических идентификаторов для набора целых значений.
Общий синтаксис для задания переменной-перечисления подобен синтаксису описания структурной переменной: сначала задается новый тип, а затем описывается переменная.
enum [ имя_шаблона ] { имя_1 [ константа_1], имя_2 [константа_2 ]...};
где имя_шаблона – необязательное имя конструируемого типа;
имя_1, имя_2,... – символические имена перечисляемых констант;
константа_1, константа_2,... – необязательные значения, задающие значения перечисляемым константам (скобками [] выделены необязательные элементы).
Как и для имен шаблонов структур, можно задать шаблон перечисления и без имени, но такой "безымянный" шаблон должен быть единственным в пределах видимости (блок, файл).
При описании переменной-перечисления задается имя нового типа, а затем – имя переменной. Например:
enum days {monday=1, tuesday, wednesday,
thurday, friday, saturday, sunday
} week_day, anyday;
Здесь описываются две переменные-перечисления week_day и anyday по шаблону, имя которого days. Используя директиву typedef, можно сократить время на описание новых переменных. Например:
typedef епит { monday = 1, tuesday, wednesday,
thurday, friday, saturday, sunday
} DAYS;
DAYS week_day, anyday;
Переменной-перечислению могут присваиваться только значения, задаваемые перечисляемыми константами. Значения перечисляемым константам задаются либо явной инициализацией, либо в случае ее отсутствия – по умолчанию. В случае отсутствия явных инициализаторов первой перечисляемой константе ставится в соответствие значение 0, а каждая следующая перечисляемая константа принимается на единицу большей, чем предыдущая. Таким образом, в приведенном выше примере перечисления DAYS символическая константа monday будет равна 1, a sunday – 7.
Число байтов, выделяемых под переменную-перечисление, зависит от значения перечисляемых констант. Если все значения могут быть представлены данными типа unsigned char, то под переменную выделяется один байт. В частности, описанные ранее week_day и anyday будут занимать в памяти 1 байт. В противном случае используются 2 байта, а значения переменной-перечисления имеют тип int.
Имя переменной-перечисления может использоваться всюду в выражениях и описаниях типов. Но присваивать такой переменной можно только значения перечисляемых констант. Например:
DAYS week_day, anyday, * ptr;
week day=monday; /* это правильно */
anyday =1; /* ошибка, несмотря на то, что Monday==1 */
ptr=&week_day; /* правильная инициализация указателя */
*ptr=friday; /* правильное использование указателя */
monday=4; /* ошибка, так как monday - это константа */
----
По материалам ОмГУПС, все права защищены...
реклама
Лента материалов
Соблюдение Правил конференции строго обязательно!
Флуд, флейм и оффтоп преследуются по всей строгости закона!
Комментарии, содержащие оскорбления, нецензурные выражения (в т.ч. замаскированный мат), экстремистские высказывания, рекламу и спам, удаляются независимо от содержимого, а к их авторам могут применяться меры вплоть до запрета написания комментариев и, в случае написания комментария через социальные сети, жалобы в администрацию данной сети.
Сейчас обсуждают