Объединения
Объединения, определяемые с помощью ключевого слова union, похожи по своему виду на структуры:
union этикетка {список_элементов] [переменные];
Отличие состоит в том, что все элементы объединения занимают одно и то же место в памяти, они перекрываются. Компилятор отводит под объединение память, достаточную для размещения наибольшего элемента.
Объединения полезны, когда требуется обеспечить своего рода “полиморфное поведение” некоторого объекта. Например, вы хотите определить тип, реализующий представление различных геометрических фигур — прямоугольников, окружностей, линий, многоугольников. В зависимости от того, чем конкретно является данная фигура, для ее описания необходимы различные наборы значений. Круг описывается иначе, чем многоугольник и т. п. И вот из структур, описывающих различные фигуры, можно в свою очередь составить обобщенный тип-объединение, который будет обрабатываться различно в зависимости от значения специального поля, определяющего род фигуры. В то же время на все фигуры можно будет ссылаться через указатели одного и того же типа, что, в частности, позволит составлять динамические связанные списки из любых фигур.
Доступ к элементам объединения (их иногда называют разделами) осуществляется так же, как и в структурах, — посредством точки или стрелки, за которыми следует имя раздела.
В заключение разговора о типах, определяемых пользователем, приведем пример законченной программы. В ней определяется тип структуры, способной хранить данные различных “графических объектов”. В программе реализованы всего два их вида — прямоугольник и текстовая метка.
Листинг 3.5. Демонстрация работы со структурами
/*
** Struct.с: Структуры и объединения.
*/
#pragma hdrstop
#include <stdio.h>
#include <conio.h>
#include <string.h>
/* Тип для определения вида объекта */
typedef enum {Rect=l, Labi) Type;
/***********************************************************
** Структура для хранения прямоугольников и текстовых меток.
*/ typedef struct _GForm { Type type;
struct _GForm *next;
/* Указатель для связанного списка. */
union {
/* Анонимное объединение. */
struct {
/* Прямоугольник. */
int left, top;
int right, bottom;
} rect;
struct {
/* Текстовая метка. */
int x, у;
char text [20];
} labi;
}
} Gform;
/****************************************
** Функция вывода данных объекта.
*/ void ShowForm(GForm *f)
{
switch (f->type) {
case Rect:
/* Прямоугольник. */
printf("Rectangle: (%d, %d) (%d, %d)\n",
f->data.rect.left, f->data.rect.top,
f->data.rect.right, f->data.rect.bottom);
break;
case Labi: /* Метка. */
printfC'Text label: (%d, %d) \"%s\"\n",
f->data.labl.x, f->data.labi.y, f->data.labi.text);
}
int main(void)
{
GForm formi, form2;
/* Инициализация первого объекта. */
forml.type = Rect;
forml.data.rect.left = 50;
forml.data.rect.top = 25;
forml.data.rect.right = 100;
forml.data.rect.bottom = 75;
/* Инициализация второго объекта. */
form2.type = Labi;
form2.data.labi.x = 60;
form2.data.labl.у = 40;
strcpy(form2.data.labi.text, "This is a Label!");
/* Распечатка... */ ShowForm(&formi);
ShowForm(&form2);
printf("\nPress any key...");
getch() ;
return 0;
}
Работу программы иллюстрирует рис. 3.5.
Рис. 3.5 Программа Struct.c (Project2)
Обратите внимание, что перечисления, структуры и объединения могут быть анонимными, т. е. не иметь имен-этикеток.
Внимательно рассмотрите определение типа Gform:
typedef struct _GForm { Type type;
struct GForm *next;
/* Указатель для связанного списка. */
union {
/* Анонимное объединение. */
struct {
/* Прямоугольник. */
int left, top;
int right, bottom;
} rect;
struct {
/* Текстовая метка. */
int x, у;
char text[20] ;
} labl;
} data;
} GForm;
Структура _Gform имеет, как таковая, три элемента: type, next (не используется) и data. Последний является анонимным объединением разделов rect и labl, каждый из которых, в свою очередь, является анонимной структурой. Элементы первой хранят значения координат верхнего левого и правого нижнего углов прямоугольника; элементами второй являются координаты, задающие положение текста, и сама текстовая строка. Получаются довольно длинные выражения для доступа к элементам данных (forml.data.rect.bottom).
Заключение
В этой весьма объемистой главе мы рассмотрели чуть ли не весь стандартный ANSI С, если не считать препроцессора. Препроцессором мы займемся в следующей главе.
Кроме того, вам предстоит еще познакомиться с расширениями С, поддерживаемыми C++Builder, но выходящими за рамки стандарта ANSI. Такие расширения, связанные в основном с особенностями процессора и операционной системы, почти всегда имеются в любой реализации языка.
О них также будет говориться в следующей главе.