Типы данных, определяемые пользователем




Структуры.

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

1) Описание (объявление структуры)

Формат описания структуры:

struct [ имя_типа ] {

тип_1 элемент_1;

тип_2 элемент_2;

...

тип_n элемент_n;

} [ список_описателей ];

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

struct Sobyt{

int hour, min;

char theme[100], name[100];

int num;

};

Имя этого типа данных – Sobyt.

Описание структуры представляет собой задание нового типа struct имя_типа и не приводит к выделению памяти, а лишь даёт информацию компилятору о типах и количестве полей. Эта информация используется компилятором при описании структурных переменных (или массивов) для резерви­рования необходимого места в памяти и организации доступа к необходимым полям структурной переменной или масива структур.

2) Выделение памяти под структуру (описание структурной переменной или массива структур)

Если нам предстоит работать только с одной строкой структуры, то создаем структурную переменную, а если с таблицей (больше одной строки), то создаем массив (вектор) структур.

Описание структурной переменной состоит из задания типа и имени структурной переменной (массива), и имеет вид

struct [ имя_типа ] имя_структурной_переменной | имя_массива;

 

То есть можно описать переменные или массивы типа (например, Sobyt), так же, как переменные и массивы встроенных (cтандартных) типов, например:

Sobyt e1, e2[10]; //структура (переменная)е1 и массив структур е2

Если структура используется только в одном месте программы, можно совместить описание типа с описанием переменных, при этом имя типа можно не указывать:

struct {

int hour, min;

char theme[100], name[100];

int num;

} e1, e2[10];

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

Sobyt *pe = new Sobyt; // структура

Sobyt *pm = new Sobyt[m]; // массив m структур

 

1) Доступ к полям структурной переменной.

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

 

Доступ к полям структурных переменных производится с помощью:

- оператора выбора - «. » (точка);

- указателя.

Оператор выбора формирует ссылку на нужное поле_i структурной переменной (массива), используется с именем переменной (массива) и имеет вид -

имя_структурной_переменной | имя_массива [номер_строки]. имя_поля_i

Такая ссылка может располагаться в любом месте, где допустимы ссылки на простые перемен­ные. Ссылка на поле структурной переменной обладает всеми свойствами обычных переменных. Например, если поле это массив символов (char name[10];), то tov1.name - указатель- константа на первый элемент этого массива, а выражение &tov1.price - есть взятие адреса первого байта поля price. Нет, также, отличий в правилах видимости и времени существования от обычных переменных.

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

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

(*указатель_на_структуру).имя_члена;

- используя, оператор указателя на структуру

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

Оператор указателя на структуру печатается на клавиатуре как сочетание знаков «минус» и «больше».

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

Примеры обращений к полям структуры:

e1.hour = 12; e1.min = 30;

strcnpy(e2[0].theme, “Новые пути развития ИТ”, 99);

pe+=3; // сдвинули указатель на 3 строки таблицы вниз

pe->num = 30; // или (*pe).num = 30; pe- указатель-переменная

pm[2].hour = 14; // или (*(pm+2)).hour = 14; pm-указатель-const

tov1.name=’’Volga’’,

tov2.price=12000;

mass_tov[1][1].name = “HONDA”;

Если элементом структуры является другая структура, то доступ к ее элементам выполняется через две операции выбора. Например:

struct A {int a; double x};

struct B {A a; double x} g[2];

g[0].a.a = 1;

g[1].x = 0.1;

 

4) Операции со структурами.

Структуры одного типа можно присваивать друг другу:

*pe = e1; pm[1] = e1; pm[4] =e2[0];

 

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

Структуру можно передавать в функцию и возвращать в качестве значения функции.

5) Ввод/вывод структур, как и массивов, выполняется поэлементно. Например:

// с использованием классов ввода-вывода <iostream.h>

cin>>e1.hour>>e1.min;

cin.getline(e1.tneme,100);

cout<<e1.hour<<’ ‘<<e1.min<<’ ‘<<e1.theme<<endl;

// с использованием ввода-вывода в стиле С (<stdio.h>)

scanf(“%d%d”, &e1.hour, &e1.min; gets(e1.theme);

printf(“%d %d %s”, e1.hour, e1.min, e1.theme);

 

Структуры (только не динамические) можно инициализировать перечислением значений их элементов, т.е. явно:

Sobyt e3 = {12, 30, “ Новые пути развития ИТ ”, 25};

 

При инициализации массивов структур следует заключать в фигурные скобки каждый элемент массива (учитывая, что многомерный массив – это массив массивов):

struct tovar1 { char name[10];

longint price;}mass_tov[2][3]= {

{{“AUDI”,50000},{“BMW”, 39100},{“CITROEN”,20000}},

{{“DAEWOO”,16000},{“HONDA”,31000},{“FORD”,38000}}

};

 

В качестве примера рассмотрим программу вывода на экран студентов группы, родившихся в определенном месяце (месяц вводим интерактивно)

# include <iostream.h>

# include <stdio.h>

# include <string.h>

# define n 25 // const int n=25; - количество студентов в группе

void main()

{struct stud {

char fam[20]; // фамилия студента

char mr[8]; // месяц рождения студента

}std[n],*p=&std[0]; // описание массива 25 структур и

//указателя на структуру

int i;

char mes[8];

for(i=0;i<n;i++,p++) // указатель сдвигается построчно

{cout<<"\n"<<"введите фамилию \n";

cin>>((*p).fam); // используем операцию разыменования

cout<<'\n'<<"введите месяц\n";

cin>>(*p).mr;

}

p-=n; // указатель возвращаем на начало списка

cout<<"\n"<<"введите искомый месяц\n";

cin>>mes;

for(i=0;i<n;i++,p++)

if(strcmp(std[i].mr,mes)==0) // сравнение месяца рождения

//каждого студента с введенным

cout<<(*p).fam<<"\n";

}

 

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

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

enum [ имя_типа ] { список_констант };

Имя_типа задается в том случае, если в программе требуется определять переменные этого типа. Компилятор обеспечивает, чтобы эти переменные принимали значения только из списка констант. Константы должны быть целочисленными и могут инициализироваться обычным образом. При отсутствии инициализатора первая константа из списка обнуляется, а каждой следующей присваивается значение на единицу большее предыдущей:

1)

Enum Err{ERR_READ, ERR_WRITE, ERR_CONVERT};

// ERR_READ=0, ERR_WRITE=1, ERR_CONVERT=2

Err error;

...

switch (error){

case ERR_READ: /* операторы */ break;

case ERR_WRITE: /* операторы */ break;

case ERR_CONVERT: /* операторы */ break;

}

 

2)

enum { two = 2, three, four, ten = 10, eleven, fifty = ten + 40};

// three = 3, four = 4, eleven = 11, fifty = 50

 

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

 

2.4.3. Структуры с битовыми полями.

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

struct [ имя_struct]{ unsigned | bool имя_поля_1: длина_поля_1;

unsigned | bool имя_поля_2: длина_поля_2;

………..

unsigned | bool имя_поля_n: длина-поля_n;};

 

Битовые поля – это особый вид полей структуры. Они используются для плотной упаковки данных, например, флажков типа “да / нет”. Минимальная адресуемая ячейка памяти – 1 байт, а для хранения флажка достаточно 1 бита. При описании битового поля после имени через двоеточие указываетсядлина поля в битах (целая константа):

struct { bool key: 1;

unsigned keycod: 3;

unsigned keytime: 5;

} mykey;

 

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

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

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

Ссылки на поле битов выполняются точно так же, как и компоненты общих структур.

Объединение (смесь).

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

union ind_un {

int i; //требуется 2 байта

double j; //требуется 8 байт

char k; //требуется 1 байт

} m, *mptr=&m;

// Для хранения переменной m типа union ind_un будет выделено 8-байт

// (значения double)

 

Объединения применяют для экономии памяти в тех случаях, когда известно, что больше одного поля одновременно не понадобится:

#include <iostream.h>

int main(){

enum paytype {CARD, CHECK};

paytype ptype;

union payment {

char card[25];

long check;} info;

/* присваивание значений info и ptype */

switch (ptype){

case CARD: cout << “Оплата по карте”<<info.card; break;

case CHECK: cout<< “Оплата чеком”<<info.check; break;

}

return 0;

}

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

По сравнению со структурами на объединения налагаются некоторые ограничения:

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

- объединение не может содержать битовые поля;

- объединение не может содержать виртуальные методы, конструкторы, деструкторы и операцию присваивания;

- объединение не может входить в иерархию классов.

 

2.4.5. Контрольные вопросы

1.Назовите основные отличительные характеристики типа данных «структура».

2.Какие типы полей может содержать структура?

3.Назовите способы обращения к элементам структуры.

4.Назовите способы описания переменных структурного типа.

5.Можно ли присваивать переменной структурного типа структуру?

6. Каковы отличия структуры от перечисления?

7. Каковы отличия структуры от смеси (объединения)?

8. Каковы отличия структуры от массива?

9. Чем отличаются указатели на массив и на структуру?

 

Функции.



Поделиться:




Поиск по сайту

©2015-2024 poisk-ru.ru
Все права принадлежать их авторам. Данный сайт не претендует на авторства, а предоставляет бесплатное использование.
Дата создания страницы: 2017-11-19 Нарушение авторских прав и Нарушение персональных данных


Поиск по сайту: