Лекция 6.

 

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

 

 

Вопросы

 

 

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

2.     Структуры

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

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 <stdio.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);

  }