Будь умным!


У вас вопросы?
У нас ответы:) SamZan.net

Она позволяет строить новые типы данных языка Си

Работа добавлена на сайт samzan.net:


Структуры

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

Описание структуры выглядит следующим образом:

struct имя_структуры {

   описание полей структуры

};

Здесь имя_структуры — это любое имя, соответствующее синтаксису языка Си, описания полей структуры — любая последовательность описаний переменных, имена и типы этих переменных могут быть произвольными. Эти переменные называются полями структуры. Заканчивается описание структуры закрывающей фигурной скобкой. За закрывающей фигурной скобкой в описании структуры обязательно следует точка с запятой, в отличие от конструкции составного оператора, не следует забывать об этом! Для чего здесь нужна точка с запятой, будет объяснено ниже в разделе "Структуры и оператор определения типа typedef".

Рассмотрим пример: опишем вектор в трехмерном пространстве, который задается тремя вещественными координатами x, y, z:

struct R3Vector {

   double x;

   double y;

   double z;

};

Таким образом, вводится новый тип " struct R3Vector "; объект этого типа содержит внутри себя три вещественных поля с именами x, y, z. После того как структура определена, можно описывать переменные такого типа, при этом в качестве имени типа следует использовать выражение struct R3Vector. Например, в следующей строке описываются два вещественных вектора в трехмерном пространстве с именами u, v:

struct R3Vector u, v;

С объектами типа структура можно работать как с единым целым, например, копировать эти объекты целиком:

struct R3Vector u, v;

. . .

u = v;  // Копируем вектор как единое целое

В этом примере вектор v копируется в вектор u ; копирование структур сводится к переписыванию области памяти. Сравнивать структуры нельзя:

struct R3Vector u, v;

. . .

if (u == v) { // Ошибка! Сравнивать структуры нельзя

   . . .

}

Имеется также возможность работать с полями структуры. Для этого используется операция точка " .": пусть s — объект типа структура, f — имя поля структуры. Тогда выражение

s.f

является полем f структуры s, с ним можно работать как с обычной переменной. Например, в следующем фрагменте в вектор w записывается векторное произведение векторов u и v трехмерного пространства: w = u x v.

struct R3Vector u, v, w;

. . .

// Вычисляем векторное произведение w = u * v

w.x = u.y * v.z - u.z * v.y;

w.y = (-u.x) * v.z + u.z * v.x;

w.z = u.x * v.y - u.y * v.x;

В приведенных примерах все поля структуры R3Vector имеют один и тот же тип double, однако это совершенно не обязательно. Полями структуры могут быть другие структуры, никаких ограничений нет. Пример: плоскость в трехмерном пространстве задается точкой и вектором нормали, ей соответствует структура R3Plane. Точке трехмерного пространства соответствует структура R3Point, которая определяется аналогично вектору. Полное описание всех трех структур:

struct R3Vector { // Вектор трехмерного пространства

   double x;

   double y;

   double z;

};

struct R3Point { // Точка трехмерного пространства

   double x;

   double y;

   double z;

};

struct R3Plane { // Плоскость в трехмерном пр-ве

   struct R3Point origin;  // точка в плоскости

   struct R3Vector normal; // нормаль к плоскости

};

Пусть plane — это объект типа плоскость. Для того, чтобы получить координату x точки плоскости, надо два раза применить операцию "точка" доступа к полю структуры:

plane.origin.x

Структуры и указатели

Указатели на структуры используются довольно часто. Указатель на структуру S описывается обычным образом, в качестве имени типа фигурирует struct S*. Например, в следующем фрагменте переменная p описана как указатель на структуру S:

struct S { . . . }; // Определение структуры S

struct S *p; // Описание указателя на структуру S

Описание структуры может содержать указатель на структуру того же типа в качестве одного из полей. Язык Си допускает использование указателей на структуры, определение которых еще не завершено. Например, рассмотрим структуру TreeNode (вершина дерева), которая используется при определении бинарного дерева (см. раздел 4.5.4). Она содержит указатели на родительский узел и на левого и правого сыновей, которые также имеют тип struct TreeNode:

struct TreeNode {   // Вершина дерева

   struct TreeNode *parent; // Указатель на отца,

   struct TreeNode *left;   //   на левого сына,

   struct TreeNode *right;  //   на правого сына

   void *value;             // Значение в вершине

};

Здесь при описании полей parent, left, right используется тип ``указатель на структуру TreeNode '', определение которой еще не завершено, что допустимо в языке Си. Возможны и более сложные комбинации, например, структура A содержит указатель на структуру B, а структура B — указатель на структуру A. В этом случае можно использовать предварительное описание структуры, например, строка

struct A;

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

struct A; // Предварительное описание структуры A

   struct B; // Предварительное описание структуры B

   struct A { // Определение структуры A

       . . .

       struct B *p; // Указатель на структуру B

       . . .

   };

   struct B { // Определение структуры B

       . . .

       struct A *q; // Указатель на структуру A

       . . .

   };

Для доступа к полям структуры через указатель на структуру служит операция стрелочка, которая обозначается двумя символами -> (минус и знак больше), их нужно рассматривать как одну неразрывную лексему (т.е. единый знак, единое слово). Пусть S — имя структуры, f — некоторое поле структуры S, p — указатель на структуру S. Тогда выражение

p->f

обозначает поле f структуры S (само поле, а не указатель не него!). Это выражение можно записать, используя операцию звездочка (доступ к объекту через указатель),

p->f    ~    (*p).f

но, конечно, первый способ гораздо нагляднее. (Во втором случае круглые скобки вокруг выражения *p обязательны, поскольку приоритет операции точка выше, чем операции звездочка.)

Структуры и оператор определения типа typedef

Синтаксис языка Си позволяет в одном предложении определить структуру и описать несколько переменных структурного типа. Например, строка

struct R2_point { double x; double y; } t, *p;

одновременно определяет структуру R2_point (точка на двумерной плоскости) и описывает две переменные t и p. Первая имеет тип struct R2_point (точка плоскости), вторая — struct R2_point * (указатель на точку плоскости). Таким образом, после закрывающей фигурной скобки может идти необязательный список определяемых переменных, причем можно использовать все конструкции Си для построения сложных типов (указатели, массивы, функции). Список всегда завершается точкой с запятой, поэтому даже при пустом списке точка с запятой после фигурной скобки обязательна.

Возможно анонимное определение структуры, когда имя структуры после ключевого слова struct опускается; в этом случае список описываемых переменных должен быть непустым (иначе такое описание совершенно бессмысленно). Пример:

struct { double x; double y; } t, *p;

Здесь имя структуры отсутствует. Определены две переменные t и p, первая имеет структурный тип с полями x и y типа double, вторая — указатель на данный структурный тип. Такие описания в чистом виде программисты обычно не используют, гораздо чаще анонимное определение структуры комбинируют с оператором определения имени типа typedef (см. c. 117. ). Например, можно определить два типа R2Point (точка вещественной двумерной плоскости) и R2PointPtr (указатель на точку вещественной двумерной плоскости) в одном предложении, комбинируя оператор typedef с анонимным определением структуры:

typedef struct {

   double x;

   double y;

} R2Point, *R2PointPtr;

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

R2Point a, b, c;    // Описываем три точки a, b, c

R2PointPtr p;       // Описываем указатель на точку

R2Point *q;         // Эквивалентно R2PointPtr q;

Сравните с описаниями, использующими приведенное выше определение структуры R2_point:

struct R2_Point a, b, c;

struct R2_Point *p;

struct R2_Point *q;

Первый способ лаконичнее и нагляднее.

Вовсе не обязательно комбинировать оператор typedef непременно с анонимным определением структуры; можно в одном предложении как определить имя структуры, так и ввести новый тип. Например, предложение

typedef struct R2_point {

   double x;

   double y;

} R2Point, *R2PointPtr;

определяет структуру R2_point, а также два новых типа R2Point (структура R2_point ) и R2PointPtr (указатель на структуру R2_point ). К сожалению, имя структуры не должно совпадать с именем типа, именно поэтому здесь в качестве имени структуры приходится использовать несколько вычурное имя R2_point. Впрочем, обычно в дальнейшем оно не нужно.

Все вышесказанное касательно языка Си справедливо и в C++. Кроме того, в C++ считается, что определение структуры S одновременно вводит и новый тип с именем S. Поэтому в случае C++ нет необходимости в использовании оператора typedef при задании структурных типов. Связано это с тем, что структура с точки зрения C++ является классом, а классы и определяемые ими типы — это основа языка C++. Сравните описания Си

struct S { ... };

struct S a, b, c;

struct S *p, *q;

и C++:

struct S { ... };

S a, b, c;

S *p, *q;

Конечно, описания C++ проще и нагляднее.

Структуры

Объявление структуры

Структуры языка C++ представляют поименованную совокупность компонентов, называемых полями, или элементами структуры. Элементом структуры может быть:

  •  переменная любого допустимого типа;
  •  битовое поле;
  •  функция.

Объявление структуры имеет следующее формальное описание:

struct [имя_структуры] {

   тип_элемента_структуры  имя_ элемента1;

   тип_элемента_структуры  имя_ элемента2;

    ...

   тип_элемента_структуры  имя_ элементаN;

} [список_объявляемых_переменных];

Объявление структуры с битовыми полями имеет следующее формальное описание:

struct [имя_структуры] {

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

       [имя_ элемента1] : число_бит;

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

       [имя_ элемента2] : число_бит;

    ...

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

       [имя_ элементаN] : число_бит;

} [список_объявляемых_переменных];

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

struct имя_структуры;

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

Список объявляемых переменных типа данной структуры может содержать:

  •  имена переменных;
  •  имена массивов;
  •  указатели.

Например: struct sA {char a[2], int i;}; struA, struB[10], *struC;

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

Размер структуры с битовыми полями всегда кратен байту. Битовые поля можно определять для целочисленных переменных типа int, unsigned int, char и unsigned char. Одна структура одновременно может содержать и переменные, и битовые поля. Если для битового поля не задано имя элемента, то доступ к такому полю не разрешен, но количество указанных бит в структуре размещается.

Типом элемента структуры может быть:

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

Например:

struct sA {char a[2], sA* this_struct;};  

   // Корректное объявление структуры

struct sB;   

   // Неполное объявление структуры

struct sA {char a[2], sB* this_struct;};  

   // Корректное объявление структуры

struct sA {char a[2], sA this_struct;};  

   // Ошибочное объявление

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

Например:

struct structA {

   struct structA *pA; int iA; } sA;

   // pA указатель на структуру

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

Например:

struct POINT   // Объявление структурного

              // типа POINT

{

  int x;      // Объявление элементов x и y

  int y;

} p_screen = { 50, 100 };

   // Эквивалентно записи p_screen.x = 50;

   // и p_screen.y = 100;

Выделение памяти

При создании переменной типа структуры:

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

Рассмотрим пример выделения памяти под структуру

struct structA {

   char cA;

   char sA[2];

   float fA;};

При создании переменной структурного типа:

structA s1;

будет выделено 7 байтов. Элементы структуры будут размещены в памяти в следующем порядке:

char cA

char sA[2]

float fA

1

2

3

4

5

6

7

Рассмотрим пример выделения памяти под структуру

struct structB {

   int i1:2;

   int i2:3;

   int :6;

   unsigned int i3:4;};

При создании переменной структурного типа:

structB s2;

будет выделено 2 байта. Элементы структуры будут размещены в памяти в следующем порядке:


Рис. 8.1. 

Для целочисленных значений, предусматривающих наличие знакового разряда (например, int ), старший левый бит из общего числа битов, выделяемых под данное битовое поле, интерпретируется как знак. Например, битовое значение 11 для поля i1 будет восприниматься как -1, а значение 11 для поля i3 - как 3.

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

Элементы структуры могут иметь модификаторы доступа: public, private и protected. По умолчанию все элементы структуры объявляются как общедоступные ( public ). Забегая вперед, следует сказать, что все члены класса по умолчанию объявляются как защищенные ( private ).

Для обращения к отдельным элементам структуры используются операторы: . и ->.

Доступ к элементам структуры может иметь следующее формальное описание:

переменная_структурного_типа.элемент_структуры=значение;

имя_структурного_типа *указатель_структуры=

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

указатель_структуры->элемент_структуры=значение;

Например:

struct structA {

 char c1;

 char s1[4];

 float f1;} aS1,    

   // aS1 - переменная структурного типа

 *prtaS1=&aS1;    

   // prtaS1 - указатель на структуру aS1

struct structB {

 struct structA aS2;    

   // Вложенная структура

} bS1,*prtbS1=&bS1;

aS1.c1= 'Е';        

   // Доступ к элементу c1 структуры aS1

prtaS1->c1= 'Е';     

   // Доступ к элементу c1 через

   // указатель prtaS1

(*prtaS1).c1= 'Е';    

   // Доступ к элементу c1

(prtbS1->aS2).c1='Е';

   // Доступ к элементу вложенной структуры

Доступ к элементу массива структурного типа имеет следующий формальный синтаксис:

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

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

Например:

struct structA {

   int i; char c;} sA[4], *psA;

psA=&sA[0];

cout<<psA->i;    

 // Доступ к первому элементу массива

 // структур

 // Переход ко второму элементу массива

psA++;        

 // Эквивалентно записи:  psA=&sA[1];

cout<<psA->i;

Передача структур в качестве параметров

Переменные структурного типа и элементы структуры можно передавать в функции в качестве параметров.

Передача параметров может выполняться:

  •  по ссылке или указателю;
  •  по значению.

При передаче параметра по указателю передается только указатель на структуру, при передаче по значению в стек копируется все содержание структуры.

Например:

struct structA {

   int i; char c;} sA, *psA=&sA;

void F1(struct structA sA);    

   // Передача параметров по значению

void F2(struct structA *psA);    

   // Передача параметров по указателю

void F3(struct structA &sA);

   // Передача параметров по ссылке

void F2(struct structA *psA) {

   psA->i =10; }     

   // Доступ к элементу структуры

При большой вложенности вызовов и использовании большого числа структур или их значительных размерах вызов по значению может привести к переполнению стека.

Функция может возвращать значение структурного типа или типа указателя на структуру.

Например:

struct structA { int i; char с;};

struct structA Function3(void);    

   // Функция возвращает значение

   // структурного типа

struct structA *Function4(void);

   // Функция возвращает указатель

   // на структуру

Объединения

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

Использование объединений значительно экономит память, выделяемую под объекты.

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

Так, компилятор Visual C++ выделит 4 байта под следующее объединение:

union unionA {

   char ch1;

   float f1;} a1;

Элементы объединения

Количество занимаемых байтов

char ch1

1

float f1

1

2

3

4

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

Инициализировать объединение при его объявлении можно только заданием значения первого элемента объединения.

Например:

union unionA {

   char ch1;

 float f1;} a1={ 'M' };

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

Например:

union TypeNum

{

  int    i;

  long  l;  

  float  f;  

};

  union TypeNum vNum = { 1 };

  // Инициализация первого элемента объединения i = 1

  cout<< vNum.i;

  vNum.f = 4.13;

  cout<< vNum.f;

Элементы объединения не могут иметь модификаторов доступа и всегда реализуются как общедоступные ( public ).

Перечисления

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

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

Перечисление может иметь следующее формальное описание:

enum имя_типа {список_значений}

   список_объявляемых_переменных;

enum имя_типа список_объявляемых_переменных;

enum (список_элемент=значение);

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

Значение каждого нумератора определяется как значение типа int. По умолчанию первый нумератор определяется значением 0, второй - значением 1 и т.д. Для инициализации значений нумератора не с 0, а с другого целочисленного значения, следует присвоить это значение первому элементу списка значений перечислимого типа.

Например:

// Создание перечисления

enum eDay{sn, mn, ts, wd, th, fr, st} day1;

   // переменная day1 будет принимать

   // значения в диапазоне от 0 до 6

day1=st;       

   // day1 - переменная перечислимого типа

int i1=sn;   

   // i1 будет равно 0

day1= eDay(0);      

   // eDay(0) равно значению sn

enum(color1=255);        

   // Объявление перечисления, определяющего

   // именованную целую константу color1

int icolor=color1;

enum eDay2{sn=1, mn, ts, wd, th, fr, st} day2;

   // переменная day2 будет принимать

   // значения в диапазоне от 1 до 7

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

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




1. 45Общеевропейские цели страхования 1992 был создан свободный и открытый рынок ЕС12 гос
2. Международные операции по обмену объектами интеллектуальной собственности
3. День суда увидят только худшие из людей поэтому люди которые будут жить когда Час тот начнется являются х
4. ТЕМА - ПРОВЕДЕННЯ ТЕХНІЧНОГО НІВЕЛЮВАННЯ План Розбивка і закріплення трас на місцевості
5. тема образования в высшей школе в последние годы претерпела существенные изменения
6. Тема- Раціональне та здорове харчування людини Мета роботи- Засвоїти теоретичні відомості про раціональне
7. Параллелограмм развития памяти
8. Обломов длится 12 лет
9. первых происходит возрождение интереса к античной культуре в целом ~ к античной философии к античным религ
10.  Галузь застосування ДСанПіН поширюються на загальноосвітні навчальні заклади I III IIII ступенів спеціалі
11. Тема 8 Сучасна світова філософія План ldquo;Філософія життяrdquo;- В
12.  Отчет о движении денежных средств если он используется вкупе с остальными формами финансовой отчетности
13. тематическая статистикаГруппа- КВТ111пересд ТВДата тестирования- 31
14. задание 30 вариант N 100 Xmin 9 Xmx 72 R Xmx ~ Xmin 63 K 332 lg 100 1 8 H R-K 7
15. Практикум курсанта слушателя
16. географічним положенням різноманітним господарством розгалуженою системою зовнішньоекономічних зв~язкі
17. Состояние водных ресурсов России и Сибири.html
18. Контрольная по русскому языку
19. язаний з розвитком художньої літератури
20. Ганди Вопросы философии