Лекция 6. Перечислимый тип. Структуры. Объединения

1. Перечислимый тип

Перечислимый тип задаёт тип, который является подмножеством целого типа.

Объявление переменной перечислимого типа задаёт имя переменной и определяет список именованных констант, называемый списком перечисления:
enum [<тег>] {<список перечисления>} <описатель> [, <описатель> ...]; enum <тег> <описатель> [, <описатель> ...];

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

Список перечисления содержит одну или более конструкций вида:
<идентификатор> [= <константное выражение>]

Конструкции в списке разделяются запятыми. Каждый идентификатор именует элемент списка перечисления. По умолчанию, если не задано константное выражение, первому элементу присваивается значение 0, следующему элементу – значение 1 и т.д.

Запись = <константное выражение> изменяет умалчиваемую последовательность значений. Элемент, идентификатор которого предшествует записи = <константное выражение>, принимает значение, задаваемое этим константным выражением. Константное выражение должно иметь тип int и может быть как положительным, так и отрицательным. Следующий элемент списка получает значение, равное <константное выражение> + 1, если только его значение не задаётся явно другим константным выражением.

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

enum Weekdays {SA, SU, MO, TU, WE, TH, FR};
enum Weekdays {SA, SU = 0, MO, TU, WE, TH, FR}; // SA и SU имеют одинаковое значение
void main()
{ enum Weekdays d1 = SA, d2 = SU, d3 = WE, d4;
d4 = 2; // Ошибка!
d4 = d1 + d2; // Ошибка!
d4 = (enum Weekdays)(d1 + d2); // Можно, но результат
d4 = (enum Weekdays)(d1 - d2); // может не попасть
d4 = (enum Weekdays)(TH * FR); // в область определения
d4 = (enum Weekdays)(WE / TU); // перечисления
}

2. Структуры

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

Объявление структуры задает имя структурного типа и/или последовательность объявлений переменных, называемых элементами структуры. Эти элементы могут иметь различные типы.
struct [<тег>] {<список объявлений элементов>} <описатель> [, <описатель> ...]; struct <тег> <описатель> [, <описатель> ...];

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

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

struct
{ char str[50];
int a, b; // Объявляем структуру, не задавая тег
} s; // и сразу же объявляем переменную
struct S
{ char str[50];
int a, b;
}; // Объявляем структуру с тегом S
struct S s; // Объявляем переменную

Элемент структуры не может быть структурой того же типа, в которой он содержится. Однако он может быть указателем на тип структуры, в которую он входит. Размер указателя стандартный, поэтому компилятор знает, сколько памяти потребуется под указатель. Для работы с указателем надо знать размер типа, на который он указывает, но к моменту работы с указателем структура будет полностью объявлена, и, следовательно, размер её будет известен.

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

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

struct S s = {"Str", 0, 1}; // Используем тег S, объявленный в предыдущем примере

Выбор элемента структуры осуществляется с помощью одной из следующих конструкций:
<переменная> . <идентификатор элемента структуры> <указатель> -> <идентификатор элемента структуры>

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

struct S s, *p = &s; // Объявляем переменную s и указатель p, в который заносим адрес переменной s
s.a = 10;
p->b = 20;

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

2.1. Пример

Вводим массив структур и осуществляем поиск по любой совокупности параметров. #include <sspanio.h> #include <string.h> #include <conio.h> struct S // Объявляем структуру, состоящую { char str[21]; // из строки и целого числа int a; }; struct S s[10]; // Объявляем массив структур void main(int argc, char *argv[]) { int n, a, i, check = 0; // Переменная а содержит число, которое будет сравниваться с полем структуры а. // Переменная check указывает, нужно ли использовать этот параметр для поиска. char str[21] = ""; // Переменная str содержит строку, которая будет сравниваться с полем структуры str. // Если переменная str содержит пустую строку, этот параметр не используется для поиска. FILE *in, *out; char ans; if (argc < 3) { printf("Too few arguments.\n"); return; } if ((in = fopen(argv[1], "r")) == NULL) { printf("It is impossible to open file '%s'.\n", argv[1]); return; } if ((out = fopen(argv[2], "w")) == NULL) { printf("It is impossible to open file '%s'.\n", argv[2]); fclose(in); return; } for (n = 0; !feof(in); n++) fscanf(in, "%s%d", s[n].str, &s[n].a); fclose(in); printf("Use Str for search? "); ans = getche(); if (ans == 'y' || ans == 'Y') { printf("\nInput string for search: "); scanf("%s",str); } printf("Use A for search? "); ans = getche(); if (ans == 'y' || ans == 'Y') { check = 1; printf("\nInput A: "); scanf("%d", &a); } for (i = 0; i < n; i++) if ((!*str || strcmp(str, s[i].str) == 0) && // Данное условие проверяет содержимое структуры на равенство (!check || a == s[i].a)) // параметрам поиска, учитывая необходимость сравнения с этим параметром fprintf(out, "%-30s %3d\n", s[i].str, s[i].a); fclose(out); }

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

Объединение позволяет в разные моменты времени хранить в одном объекте значения разных типов. В процессе объявления объединения с ним ассоциируется набор типов значений, которые могут храниться в данном объединении. В каждый момент времени объединение может хранить значение только одного типа из набора. Контроль над тем, какого типа значение хранится в данный момент в объединении, возлагается на программиста.
union [<тег>] {<список объявлений элементов>} <описатель> [, <описатель> ...]; union <тег> <описатель> [, <описатель> ...];

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

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

#include <stdio.h> void main() { union { float f; long int i; } u; printf("Input float number: "); scanf("%f", &u.f); printf("Internal: %08x\n\n", u.i); }