Лекция 1

Правила оформления программ на Си/Си++.
Операции и основные типы данных.

Лекция 2

Операторы


Лекция 3

Использование библиотечных процедур СИ

 

Лекция 10

Директивы препроцессора

Лекция 11

Видимость и время жизни переменных

Лекция 12

Дополнительные возможности Си++


Лекция 13

Объектно-ориентированное программирование
 

 

 

главная

Лекция

Лекция №9

Тема: Структуры


Содержание:

Структура

Имя структуры, член

Структуры и функции

Массивы структур

Указатели на структуры

Структуры, ссылающиеся на себя

Объединения

 

Структура

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

Традиционным примером структуры является учетная карточ-
ка работающего: "служащий" описывается набором атрибутов та-
ких, как фамилия, имя, отчество (ф.и.о.), адрес, код соци-
ального обеспечения, зарплата и т.д. Некоторые из этих атри-
бутов сами могут оказаться структурами: ф.и.о. Имеет нес-
колько компонент, как и адрес, и даже зарплата.


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


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

struct date {
int day;
int month;
int year;
int yearday;
char mon_name[4];};



Описание структуры, состоящее из заключенного в фигурные
скобки списка описаний, начинается с ключевого слова struct.
За словом struct может следовать необязательное имя, называ-
емое tag (ярлыком) структуры (здесь это DATе). Ярлык именует
структуры этого вида и может использоваться в дальнейшем как
сокращенная запись подробного описания.


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

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

struct { ...} X,Y,Z;

синтаксически аналогичен

int X,Y,Z;

в том смысле, что каждый из операторов описывает X , Y и Z в
качестве переменных соотвествующих типов и приводит к выде-
лению для них памяти.


Описание структуры, за которым не следует списка пере-
менных, не приводит к выделению какой-либо памяти; оно толь-
ко определяет шаблон или форму структуры. Однако, если такое
описание снабжено ярлыком, то этот ярлык может быть исполь-
зован позднее при определении фактических экземпляров струк-
тур. Например, если дано приведенное выше описание date, то

struct date D;

определяет переменную D в качестве структуры типа date.
Внешнюю или статическую структуру можно инициализировать,
поместив вслед за ее определением список инициализаторов для
ее компонент:

struct date D={ 4, 7, 1776, 186, "JUL"};

Член определенной структуры может быть указан в выраже-
нии с помощью конструкции вида:

Имя структуры.Член

Операция указания члена структуры "." связывает имя структу-
ры и имя члена. В качестве примера определим leap (признак
високосности года) на основе даты, находящейся в структуре
D,


leap=d.year%4==0&&d.year%100!=0
||d.year%400==0;


или проверим имя месяца

if(strcmp(d.mon_name,"aug")==0) ...

Или преобразуем первый символ имени месяца так, чтобы оно
начиналось со строчной буквы

d.mon_name[0]=lower(d.mon_name[0]);

Структуры могут быть вложенными; учетная карточка служа-
щего может фактически выглядеть так:




struct person {
char name[namesize];
char address[adrsize];
long zipcode; /* почтовый индекс */
long ss_number; /* код соц. Обеспечения */
double salary; /* зарплата */
struct date birthdate;/* дата рождения */
struct date hiredate;/* дата поступления
на работу */ };

Структура person содержит две структуры типа date. Если мы
определим emp как

struct person emp;
то

emp.birthdate.month


будет ссылаться на месяц рождения. Операция указания члена
структуры "." ассоциируется слева направо.

Структуры и функции.


В языке "C" существует ряд ограничений на использование
структур. Обязательные правила заключаются в том, что единс-
твенные операции, которые вы можете проводить со структура-
ми, состоят в определении ее адреса с помощью операции & и
доступе к одному из ее членов. Это влечет за собой то, что
структуры нельзя присваивать или копировать как целое, и что
они не могут быть переданы функциям или возвращены ими. На указа-
тели структур эти ограничения однако не накладываются, так
что структуры и функции все же могут с удобством работать
совместно. И наконец, автоматические структуры, как и авто-
матические массивы, не могут быть инициализированы; инициа-
лизация возможна только в случае внешних или статических
структур.

Описание

struct date *pd;

говорит, что PD является указателем структуры типа DATE.
Запись, показанная на примере

pd->year


является новой. Если P - указатель на структуру, то
P->член структуры
------------------
обращается к конкретному члену. (Операция -> - это знак ми-
нус, за которым следует знак ">".)
Так как PD указывает на структуру, то к члену year можно
обратиться и следующим образом

(*pd).year

но указатели структур используются настолько часто, что за-
пись -> оказывается удобным сокращением. Круглые скобки в
(*PD).year необходимы, потому что операция указания члена
стуктуры старше , чем * . Обе операции, "->" и ".", ассоции-
руются слева направо, так что конструкции слева и справа
зквивалентны

P->Q->memb (P->Q)->memb
emp.birthdate.month (emp.birthdate).month

Для полноты ниже приводится другая функция, month_day, пере-
писанная с использованием структур.



month_day(struct date *PD) /* set month and day from day of year */
{
int I, leap;

leap = PD->YEAR % 4 == 0 && PD->YEAR % 100 != 0
|| PD->year % 400 == 0;
PD->day = PD->yearday;
for (I = 1; PD->day> day_tab[leap][I]; I++)
PD->day -= day_tab[leap][I];
PD->month = I;
}



Операции работы со структурами "->" и "." наряду со ()
для списка аргументов и [] для индексов находятся на самом
верху иерархии страшинства операций и, следовательно, связы-
ваются очень крепко. Если, например, имеется описание


struct {
int X;
int *Y;
} *P;


то выражение

++P->X

увеличивает X а не P, так как оно эквивалентно выражению
++(P->X). Для изменения порядка выполнения операций можно
использовать круглые скобки: (++P)->X увеличивает P до дос-
тупа к х, а (P++)->X увеличивает P после.
Совершенно аналогично *P->Y извлекает то, на что указы-
вает Y; *P->Y++ увеличивает Y после обработки того, на что
он указывает (точно так же, как и *S++); (*P->Y)++ увеличи-
вает то, на что указывает Y; *P++->Y увеличивает P после вы-
борки того, на что указывает Y.

Массивы сруктур.


Структуры особенно подходят для управления массивами
связанных переменных. Рассмотрим, например, программу подс-
чета числа вхождений каждого ключевого слова языка "C".


char *keyword [nkeys];
int keycount [nkeys];


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


char *keyword;
int keycount;


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


struct key {
char *keyword;
int keycount;
} keytab [nkeys];


опрeделяет массив keytab структур такого типа и отводит для
них память. Каждый элемент массива является структурой. Это
можно было бы записать и так:


struct key {
char *keyword;
int keycount; };
struct key keytab [nkeys];


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


struct key {
char *keyword;
int keycount; }keytab
[] ={
"break", 0,
"case", 0,
"char", 0,
"continue", 0,
"default", 0,
/* ... */
"unsigned", 0,
"while", 0 };


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


{ "break", 0 },
{ "case", 0 },

. .

.
Но когда инициализаторы являются простыми переменными или
символьными строками и все они присутствуют, то во внутрен-
них фигурных скобках нет необходимости. Как обычно, компиля-
тор сам вычислит число элементов массива keytab, если иници-
ализаторы присутствуют, а скобки [] оставлены пустыми.
Программа подсчета ключевых слов начинается с определе-
ния массива keytab. ведущая программа читает свой файл вво-
да, последовательно обращаясь к функции getword, которая из-
влекает из ввода по одному слову за обращение. Каждое слово
ищется в массиве keytab с помощью варианта функции бинарного
поиска.

#define maxword 20

main() /* COUNT "C" KEYWORDS */

{

int N, T;
char word[maxword];

while ((T = getword(word,maxword)) != eof)
if (T == letter)
if((n = binary(word,keytab,nkeys)) >= 0)
keytab[n].keycount++;
for (n =0; n< nkeys; n++)
if (keytab[n].keycount> 0)
printf("%4D %S\N",
keytab[n].keycount, keytab[n].keyword);
}
binary(char *word, struct keytab[],int n)
/* FIND WORD IN TAB[0]...TAB[N-1] */
{
int low, high, mid, cond;

low = 0;
high = N - 1;
while (low<= high) {
mid = (low+high) / 2;
if((cond = strcmp(word, tab[mid].keyword)) < 0)
high = mid - 1;
else if (cond> 0)
low = mid + 1;else
return (mid);
}
return(-1);
}
 

getword возвращает letter каждый раз, как она находит слово,
и копирует это слово в свой первый аргумент.
Величина nkeys - это количество ключевых слов в массиве
keytab . Хотя мы можем сосчитать это число вручную, гораздо
легче и надежнее поручить это машине, особенно в том случае,
если список ключевых слов подвержен изменениям. Одной из
возможностей было бы закончить список инициализаторов указа-
нием на нуль и затем пройти в цикле сквозь массив keytab,
пока не найдется конец.
Но, поскольку размер этого массива полностью определен к
моменту компиляции, здесь имеется более простая возможность.
Число элементов просто есть

size of keytab / size of struct key

дело в том, что в языке "C" предусмотрена унарная операция
sizeof, выполняемая во время компиляции, которая позволяет
вычислить размер любого объекта. Выражение

sizeof(object)

выдает целое, равное размеру указанного объекта. (Размер оп-
ределяется в неспецифицированных единицах, называемых "бай-
тами", которые имеют тот же размер, что и переменные типа
char. Объект может быть фактической переменной, массивом и
структурой, или именем основного типа, как int или double,
или именем производного типа, как структура.
Число ключевых слов равно размеру массива, деленному на раз-
мер одного элемента массива. Это вычисление используется в
утверждении #define для установления значения nkeys:

#define nkeys (sizeof(keytab) / sizeof(struct key))

getword возвращает следующее "слово" из ввода, где словом
считается либо строка букв и цифр, начинающихся с буквы, ли-
бо отдельный символ. Тип объекта возвращается в качестве зна-
чения функции; это - letter, если найдено слово, eof для
конца файла и сам символ, если он не буквенный.



getword(char *W, INT LIM) /* GET NEXT WORD FROM INPUT */
{
int C, T;
if (type(C=*W++=getch()) !=letter) {
*W='\0';
return(C);
}
while (--LIM > 0) {
T = type(C = *W++ = getch());
if (T ! = letter&& T ! = digit) {
ungetch(C);
break;
}
}
*(W-1) - '\0';
return(letter);
}



Функция getword использует функции getch и ungetch: когда
набор алфавитных символов прерывается, функция getword
получает один лишний символ. В результате вызова ungetch
этот символ помещается назад во ввод для следующего обращения.
Функция getword обращается к функции type для определе-
ния типа каждого отдельного символа из файла ввода. Вот ва-
риант, справедливый только для алфавита ASCII.



tyep(int C) /* RETURN TYPE OF ASCII CHARACTER */
{
if (C>= 'A' && C<= 'Z' || C>= 'A' && C<= 'Z')
return(letter);
else if(C>= '0' && C<= '9')
return(DIGIT);
else
return(C);
}



Символические константы letter и digit могут иметь любые
значения, лишь бы они не вступали в конфликт с символами,
отличными от буквенно-цифровых, и с eof; очевидно возможен
следующий выбор

#define letter 'A'
#define digit '0'


функция getword могла бы работать быстрее, если бы обращения
к функции type были заменены обращениями к соответствующему
массиву type[ ]. В стандартной библиотеке языка "C" предус-
мотрены макросы isalpha и isdigit, действующие необходимым
образом.


Указатели на структуры.


Чтобы проиллюстрировать некоторые соображения, связанные
с использованием указателей и массивов структур,
снова составим программу подсчета ключевых строк, используя
на этот раз указатели, а не индексы массивов.
Внешнее описание массива keytab не нужно изменять, но
функции main и binary требуют модификации.



main() /* COUNT C KEYWORD; POINTER VERSION */
{
int T;
char word[maxword];
struct key *binary(), *P;
while ((T = getword(word, maxword) !=eof)
if (T==letter)
if ((P=binary(word,keytab,nkeys)) !=NULL)
P->keycount++;
for (P=keytab; P>keytab + nkeys; P++)
if (P->keycount> 0)
printf("%4D %S/N", P->keycount, P->keyword);
}
struct key *binary(char *word, struct key tab[], int N) /* FIND WORD */
{
int COND;
struct key *low = &TAB[0];
struct key *high = &TAB[N-1];
struct key *mid;
while (low<= high) {
mid = low + (high-low) / 2;
if ((cond = strcmp(WORD, MID->KEYWORD)) < 0)
high = MID - 1;
else if (COND > 0)
low = MID + 1;
else
return(MID);
}
return(NULL); }

 

Здесь имеется несколько моментов, которые стоит отме-
тить. Во-первых, описание функции binary должно указывать,
что она возвращает указатель на структуру типа key, а не на
целое; это объявляется как в функции main, так и в binary.
Если функция binary находит слово, то она возвращает указа-
тель на него; если же нет, она возвращает NULL.
Во-вторых, все обращения к элементам массива keytab осу-
ществляются через указатели. Это влечет за собой одно сущес-
твенное изменение в функции binary: средний элемент больше
нельзя вычислять просто по формуле

mid = (low + high) / 2

потому что сложение двух указателей не дает какого-нибудь
полезного результата (даже после деления на 2) и в действи-
тельности является незаконным. эту формулу надо заменить на

mid = low + (high-low) / 2

в результате которой mid становится указателем на элемент,
расположенный посередине между low и high.
Вам также следует разобраться в инициализации low и
high. Указатель можно инициализировать адресом ранее опреде-
ленного объекта; именно как мы здесь и поступили.
В функции main написали

for (P=keytab; P < keytab + nkeys; P++)

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

Структуры, ссылающиеся на себя.


Предположим, что надо справиться с более общей зада-
чей, состоящей в подсчете числа появлений всех слов в неко-
тором файле ввода. Так как список слов заранее не известен,
не можем их упорядочить удобным образом и использовать
бинарный поиск. Мы даже не можем осуществлять последователь-
ный просмотр при поступлении каждого слова, с тем чтобы ус-
тановить, не встречалось ли оно ранее; такая программа будет
работать вечно. (Более точно, ожидаемое время работы растет
как квадрат числа вводимых слов). Как же нам организовать
программу, чтобы справиться со списком произвольных слов?


Одно из решений состоит в том, чтобы все время хранить
массив поступающих до сих пор слов в упорядоченном виде, по-
мещая каждое слово в нужное место по мере их поступления.
Oднако это не следует делать, перемещая слова в линейном
массиве, - это также потребует слишком много времени. Вместо
этого используем структуру данных, называемую доичным де-
ревом.
Каждому новому слову соответствует один "узел" дерева;


каждый узел содержит:


указатель текста слова
----------------------
счетчик числа появлений
-----------------------
указатель узла левого потомка
-----------------------------
указатель узла правого потомка
------------------------------


Никакой узел не может иметь более двух детей; возможно от-
сутсвие детей или наличие только одного потомка.
Узлы создаются таким образом, что левое поддерево каждо-
го узла содержит только те слова, которые меньше слова в
этом узле, а правое поддерево только те слова, которые боль-
ше.

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

Если слова совпадают, то вопрос решается утвердительно.

Если новое слово меньше слова в дереве, то переходят к рассмотрению левого потомка; в противном случае исследуется правый потомок. Если в нужном направлении потомок отсутствует, то значит новое слово не находится в дереве
и место этого недостающего потомка как раз и является местом, куда следует поместить новое слово. Поскольку поиск из любого узла приводит к поиску одного из его потомков, то сам процесс поиска по существу является рекурсивным. В соответс-
твии с этим наиболее естественно использовать рекурсивные процедуры ввода и вывода.


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



struct tnode { /* THE BASIC NODE */
char *word; /* POINTS TO THE TEXT */
int count; /* NUMBER OF OCCURRENCES */
struct tnode *left; /* LEFT CHILD */
struct tnode *right; /* RIGHT CHILD */ };



Это "рекурсивное" описание узла может показаться рискован-
ным, но на самом деле оно вполне корректно. Структура не
имеет права содержать ссылку на саму себя, но

struct tnode *left;

описывает left как указатель на узел, а не как сам узел.
Текст самой программы оказывается удивительно маленьким,
если, конечно, иметь в распоряжении набор написанных нами
ранее процедур, обеспечивающих нужные действия. Мы имеем в
виду функцию getword для извлечения каждого слова из файла
ввода и функцию alloc для выделения места для хранения слов.
Ведущая программа просто считывает слова с помощью функ-
ции getword и помещает их в дерево, используя функцию tree.



#define MAXWORD 20
main() /* WORD FREGUENCY COUNT */

{
struct tnode *root, *tree();
char word[maxword];
int T;
root = NULL;
while ((T = getword(word, maxword)) != eof)
if (T == letter)
root= tree(root, word);
treeprint(root);
}



Функция tree сама по себе проста. Слово передается функ-
цией main к верхнему уровню (корню) дерева. На каждом этапе
это слово сравнивается со словом, уже хранящимся в этом уз-
ле, и с помощью рекурсивного обращения к tree просачивается
вниз либо к левому, либо к правому поддереву. В конце концов
это слово либо совпадает с каким-то словом, уже находящимся
в дереве (в этом случае счетчик увеличивается на единицу),
либо программа натолкнется на нулевой указатель, свидетель-
ствующий о необходимости создания и добавления к дереву но-
вого узла. В случае создания нового узла функция tree возв-
ращает указатель этого узла, который помещается в родитель-
ский узел.



struct tnode *tree(struct tnode *P,char *W)
{
struct tnode *talloc();
char *strsave();
int cond;
if (P == NULL) { /* A NEW WORD HAS ARRIVED */
P == talloc(); /* MAKE A NEW NODE */
P->word = strsave(W);
P->count = 1;
P->left = P->right = NULL;}
else if((cond = strcmp(W, P->WORD)) == 0)
P->count++; /* REPEATED WORD */
else if (cond< 0)/* LOWER GOES INTO LEFT SUBTREE */
P->left = tree(P->left, W);
else /* GREATER INTO RIGHT SUBTREE */
P->RIGHT = tree(P->right, W);
return(P);}

 

Память для нового узла выделяется функцией talloc, явля-
ющейся адаптацией для данного случая функции alloc.Она возвращает
указатель свободного пространства, пригодного для хранения
нового узла дерева. Новое слово копируется функцией
strsave в скрытое место, счетчик инициализируется еди-
ницей, и указатели обоих потомков полагаются равными нулю.


Эта часть программы выполняется только при добавлении нового
узла к ребру дерева. Мы здесь опустили проверку на ошибки
возвращаемых функций strsave и talloc значений (что неразум-
но для практически работающей программы).


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



treeprint (struct tnode*P) /* PRINT TREE P RECURSIVELY */
{
if (P !=NULL) {
treeprint (P->left);
printf("%4D %S\N", P->count, P->word);
treeprint (P->right);
}
}



Практическое замечание: если дерево становится "несба-
лансированным" из-за того, что слова поступают не в случай-
ном порядке, то время работы программы может расти слишком
быстро. В худшем случае, когда поступающие слова уже упоря-
дочены, настоящая программа осуществляет дорогостоящую ими-
тацию линейного поиска. Существуют различные обобщения дво-
ичного дерева, особенно 2-3 деревья и AVL деревья, которые
не ведут себя так "в худших случаях", но мы не будем здесь
на них останавливаться.

Объединения.


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


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



union U_TAG {
int IVAL;
float FVAL;
char *PVAL;
} UVAL;



Переменная UVAL будет иметь достаточно большой размер,чтобы
хранить наибольший из трех типов, независимо от машины, на
которой осуществляется компиляция, - программа не будет за-
висить от характеристик аппаратных средств. Любой из этих
трех типов может быть присвоен UVAR и затем использован в
выражениях, пока такое использование совместимо: извлекаемый
тип должен совпадать с последним помещенным типом. Дело
программиста - следить за тем, какой тип хранится в объеди-
нении в данный момент; если что-либо хранится как один тип,
а извлекается как другой, то результаты будут зависеть от
используемой машины.


Синтаксически доступ к членам объединения осуществляется
следующим образом:


имя объединения.член
--------------------
или
указатель объединения ->член
----------------------------


то есть точно так же, как и в случае структур. если для отс-
леживания типа, хранимого в данный момент в UVAL, использу-
ется переменная UTYPE, то можно встретить такой участок
программы:



if (UTYPE == INT)
printf("%D\N", UVAL.IVAL);
else if(UTYPE == float)
printf("%F\N", UVAL.FVAL);
else if (UTYPE == string)
printf("%s\n", UVAL.PVAL);
else
printf("BAD TYPE %d IN UTYPE\n", UTYPE);


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



struct {
char *NAME;
int FLAGS;
int UTYPE;
union {
int IVAL;
float FVAL;
char *PVAL;
} UVAL;
} symtab[NSYM];


на переменную IVAL можно сослаться как

symtab[I].UVAL.IVAL

а на первый символ строки PVAL как

*symtab[I].UVAL.PVAL

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


 

Hosted by uCoz