Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Скачиваний:
12
Добавлен:
13.02.2016
Размер:
128.48 Кб
Скачать

Структурыи объединения

1

СТРУКТУРЫ И ОБЪЕДИНЕНИЯ

Из основных типов языка Си пользователь может конструировать производные типы, такие как указатели, массивы, функции, структуры и объединения.

Будем считать, что структура - это объединенное в единое целое множество поименованных элементов в общем случае разных типов. Сравнивая структуру с массивом, следует отметить, что массив - это совокупность однородных объектов, имеющая общее имя - идентификатор массива, а компоненты структуры могут быть разных типов и все должны иметь различные имена.

Данные базовых типов считаются скалярными данными. Массивы и структуры являются агрегирующими типами, а объединения и скалярные данные - относятся к неагрегирующим типам. Агрегирующие типы включают несколько однотипных элементов, для которых выделяется такое необходимое количество памяти, в котором разместятся одновременно значения всех элементов.

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

Структуры и их определение

Пусть, например, библиотечная карточка каталога должна включать сведения, которые приведены для книг в списке литературы. Таким образом, для каждой книги будет указываться следующая информация:

фамилия и инициалы автора;

заглавие книги;

год издания;

Если к библиографической карточке каталога нужно обращаться как к единому целому, то удобно объединить такие разнородные данные удобно с помощью структуры. Каждая структура включает в себя один или несколько объектов (переменных, массивов, указателей, структур и т.д.), называемых элементами структуры.

Библиографическую карточку, с помощью структуры можно представить таким структурным типом:

struct card { char

*author;

/* Ф.И.О. автора книги */

char

*title;

/*

Заголовок книги */

int

year; };

/*

Год издания */

В соответствии с синтаксисом языка определение структурного типа начинается со служебного слова struct, вслед за которым помещается выбранное пользователем имя типа. Описания элементов, входящих в структуру, помещаются в фигурные скобки, вслед за которыми ставится точка с запятой. Элементы структуры могут быть как базовых, так и производных типов.

struct имя_структурного_тuna { определения_элементов };

Определения элементов (компонентов) структурного типа внешне подобны определениям данных соответствующих типов. Однако имеется существенное отличие. При определении структурного типа его компонентам не выделяется память, и их нельзя инициализировать. Другими словами, структурный тип не является объектом.

Определив структурный тип, можно определять и описывать конкретные структуры, т.е. структурированные

объекты, например, так:

 

struct

имя_структурного_типа

список_структур;

 

 

struct card rec1, rec2, rесЗ;

Также можно ввести собственное обозначение для структурного типа используя служебное слово typedef: typedef struct {определения_элементов} обозначение_структурного_тuna;

typedef struct { char

*author;

/*

Ф.И.О. автора книги */

char

*title;

/*

Заголовок книги */

int

year; } card1;

 

/* Год издания */

С помощью этого обозначения можно вводить структуры (объекты) так же, как с обычным именем структурного типа (например, struct card в предыдущем примере).

Пример: card1 rec4, rec5;

Структурный тип, которому программист назначает имя с помощью typedef, может в то же время иметь второе имя, вводимое стандартным образом после служебного слова struct:

typedef struct card { char *author; char *title;

int year; } card1;

Здесь card - обозначение структурного типа, вводимое с помощью typedef. Имя card1 введено для того же структурного типа стандартным способом. После такого определения структуры могут вводиться как с помощью названия card1, так и с помощью обозначения того же типа struct card

Структуры могут быть определены одновременно с определением структурного типа: struct имя_структурного_типа { определения_элементов } список_структур;

Если структура определяется однократно, т.е. нет необходимости в разных частях программы определять или описывать одинаковые по внутреннему составу структурированные объекты, то можно не вводить именованный структурный тип, а непосредственно определять структуры одновременно с определением их компонентного состава ( «урезаный» вариант предыдущего случая).

struct { определения_элементов } список_структур;

Выделение памяти для структур.

При определении структуры ей выделяется такое количество памяти, чтобы могли разместиться данные всех элементов.

В зависимости от опции «выравнивать по границам слов» и от аппаратных возможностей системы возможно два случая размещения элементов структуры: непосредственно друг за другом и с выравниванием (с «дырами»). Опция «выравнивать по границам слов» переключается в настройках компилятора или управляется директивой #pragma, которая позволяет управлять опциями компилятора прямо из программы.

При выравнивание память для следующего объекта, определенного в программе, будет выделена не сразу после элемента (если он заканчивается не на границе слова), а с промежутком, оставляемым для выравнивания по

Структурыи объединения

2

принятой границе участка адресного пространства. Машинное слово на «старых» компиляторах ВС/ВС++ - 2 байта, на новых (например, версия 5.02) – 4 байта.

Необходимость выравнивания обуславливается поставленной задачей. Включение выравнивания позволяет получить более быстрый доступ к данным, а отсутствие выравнивания позволяет «плотнее» размещать данные – экономить память.

В зависимости от наличия "пропусков" между элементами изменяется общий объем памяти, выделяемый для структуры. Реальный размер памяти в байтах, выделяемый для структуры, можно определить с помощью операции

sizeof (имя_структуры) или sizeof (имя_структурного_типа)

Пример: sizeof (struct card); sizeof (rec1); sizeof rec2;

Инициализация и присваивание структур.

Для инициализации структур начальными значениями необходимо в определении конкретной структуры после ее имени и знака '=' в фигурных скобках поместить список начальных значений элементов. Например:

struct card rec1={«Петров Иван Иванович», «Изучайте С», 2002}

Вотличие от массивов, присваивание которых можно выполнить только поэлементно, стандарт Си разрешает присваивание структур. Например: rec2 = rec1;

ВС для структур не определены операции сравнения даже на равенство. И сравнивать структуры нужно только поэлементно.

Доступ к элементам структур.

Доступ к элементам (компонентам) структур можно получить с помощью уточненных имен или указателей. Уточненное имя - это выражение с двумя операндами и операцией "точка" между ними. Операция "точка" -

операция доступа к элементу структуры (или объединения). У нее самый высокий ранг наряду со скобками и операцией "стрелка" для доступа к элементам структуры через адресующий ее указатель.

Уточненное имя используется для выбора правого операнда операции "точка" из структуры (или объединения), задаваемой левым операндом. Левый операнд должен иметь структурный тип, а правый операнд должен быть именем компонента (элемента) этой структуры. Тип результата операции "точка", т.е. тип уточненного имени, - это тип именуемого ею компонента (элемента) структуры.

имя_структуры. имя_элемента

struct card rec1={«Петров Иван Иванович», «Изучайте С», 2002} rec1.author - указатель типа char* на строку «Петров Иван Иванович» rec1.title - указатель типа char* на строку «Изучайте С»

rec1.year - переменная типа int со значением 2002

Указатели на структуры определяются, как и указатели на данные других типов. Как обычно, для структурных типов название типа состоит из двух слов - служебного слова struct и имени уже определенного структурного типа, например:

card1 *book; struct card * books;

Указатели на структуры могут вводиться и для безымянных структурных типов. Если название структурного типа введено с помощью typedef, то при определении указателей название этого типа может использоваться без предшествующего служебного слова struct:

При определении указателя на структуру он может быть инициализирован: struct card *p_rec=&rec1

Указатель на структуру, настроенный на конкретную структуру (конкретный объект) того же типа,

обеспечивает доступ к ее элементам двумя способами:

 

( * указатель_на_структуру ).имя_элемента

или

указатель_на_структуру -> имя_элемента

Первый способ традиционный. Он основан на обратимости операций разыменования '*' и получения адреса '&'. Обозначив знак равенства последовательностью '==', можно таким образом формально напомнить эти правила на конкретном примере: если

p_rec == &rec1, то *p_rec == rec1

Доступ к элементам структуры rec1 с помощью разыменования адресующего его указателя p_rec можно в соответствии с приведенными соотношениями реализовать с помощью таких конструкций:

(*p_rec).author == rec1.author

(*p_rec).year == rec1.year

Наличие скобок, ограничивающих операцию разыменования (*p_rec) необходимо т.к операция «точка» имеет более высокий приоритет , чем операция разыменования.

Второй способ для доступа к элементам структур – с помощью указателя на структуру и операции «стрелка» (->). Операция «стрелка» обеспечивает доступк элементу структуры через адресующий ее указатель:

указатель_на_структуру -> имя_элемента.

Операция «стрелка» (операция косвенного выбора компонента) двуместная. Она применяется для доступа к элементу, задаваемому правым операндом (именем компонента структуры), той структуры, которую адресует левый операнд (указатель на структуру). Пример:

p_rec->author == rec1.author p_rec->year == rec1.year

В С возможно определять в качестве компонентов структур массивы и определять массивы состоящие из структур.

Пример массива состоящего из структур и доступа к отдельным элементам:

struct card recs[50]; /* Определение массива структур */

recs[0].author – компонент author типа char[] структуры типа card, являющейся первым элементом массива recs[].

Объединения и битовые поля

Со структурами "в близком родстве" находятся объединения, которые вводятся с помощью служебного слова union.

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

Структурыи объединения

3

объединения обеспечивают возможность доступа к одному и тому же участку памяти с помощью переменных разного типа. Необходимость в такой возможности возникает, например, при выделении из внутреннего представления целого числа его отдельных байтов.

Именованный объединяющий тип вводится с помощью определения такого вида: union имя_mипa { определения элементов };

С помощью конструкции union имя_типа можно вводить объединения-объекты.

Как и для структурных типов, с помощью typedef можно вводить обозначения объединяющих типов, не требующие применения union:

typedef union имя_типа { определения элементов } обозначение_тuna;

Пример: typedef union u8

{double d ; int i[4];

char ch[8]; } u8_name;

union u8 a1,a2; u8_name a3,a4;

Объединения не относятся ни к скалярным данным, ни к данным агрегирующих типов.

Битовые поля. Битовое поле может быть только элементом структуры или объединения и вне объектов этих типов не встречается. Битовые поля не имеют адресов и не могут объединяться в массивы. Назначение битовых полей - обеспечить удобный доступ к отдельным битам данных. Кроме того, с помощью битовых полей можно формировать объекты с длиной внутреннего представления, не кратной байту. Это позволяет плотно "упаковывать" информацию, что экономит память.

Описание структуры с битовыми полями должно иметь такой формат: struct { тип_1 имя_поля_1 : ширина_поля_1;

тип_2 имя_поля_2 : ширина_поля_2; } имя_структуры ;

где тип_i - тип поля, который может быть только int, возможно, со спецификатором unsigned или signed; ширина_поля - целое неотрицательное десятичное число, значение которого обычно (в зависимости от

реализации компилятора) не должно превышать длины слова конкретной ЭВМ.

Разрешается поле без имени (для чего указываются только двоеточие и ширина), с помощью которого в структуру вводятся неиспользуемые биты (промежуток между значимыми полями). Нулевая ширина поля вводится, когда необходимо, чтобы следующее в данной структуре поле разместилось с начала очередного слова конкретной ЭВМ.

Вместо служебного слова struct может употребляться union. В этом случае определяется объединение с битовыми полями.

Для обращения к полям используются те же конструкции, что и для обращения к обычным элементам структур:

имя_структуры. имя_поля_i

(*указатель_на_структуру).имя_поля_i

указатель_на_структуру -> имя_поля_i

От реализации зависит порядок размещения в памяти полей одной структуры. Поля могут размещаться как слева направо, так и справа налево. Для компиляторов, работающих под управлением MS-DOS на IBM PC, поля, которые размещены в начале описания структуры, имеют младшие адреса. Таким образом, для примера:

/* Структура позволяющая получить двоичное представление байта-параметра */ union { unsigned char ss;

struct { unsigned а0:1; unsigned al:1; unsigned a2:1;

. . .

unsigned a7:1; } byte;

} cod;

Доступ к битам - cod.byte.a0 . . . cod.byte.a7

Доступ к числу – cod.ss

Соседние файлы в папке C_I_семестр