Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Лабы 1 курс 2 семестр / ЛР 7 Информатика 2 сем 2020

.pdf
Скачиваний:
11
Добавлен:
15.01.2021
Размер:
445.53 Кб
Скачать

8Лабораторная работа №7. Структуры и работа с ними

8.1Цели и задачи работы:

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

Задача данной лабораторной работы — выполнить выданные типовые задания (2 задачи).

8.2Теоретическая часть

Вязыке «C» типы данных можно группировать, получая новые на основе стандартных. Сгруппированные типы называются «структурой», а сами данные, находящиеся в структуре - полями. Структуры объявляются следующим образом:

struct <имя>

{

<тип1> <название_поля1>; <тип2> <название_поля2>;

...

<типN> <название_поляN>; };

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

struct MyPoint // Объявили новый тип данных «MyPoint»

{

float x; // Координата x точки float y; // Координата y точки

};

void main()

{

//Объявляем переменную point типа MyPoint

MyPoint point;

//Присваиваем значения, используя оператор

//«точка» для доступа к полям структуры point.x = 5;

128

point.y = 8;

// Получение значения поля x структуры «point» float f = point.x;

}

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

MyPoint newpoint = point;

Поля экземпляра структуры не обязательно располагаются подряд в оперативной памяти. Компилятор для оптимизации доступа к ним может произвести «выравнивание» - добавить к некоторым, меньшим по размеру, полям структуры в оперативной памяти дополнительные байты, которые хоть и не задействованы, но занимают дополнительное место. Например, размещение полей структуры

struct Test

{

char simbol; int chislo;

};

в оперативной памяти может оказаться таким:

№ байта

1

2

 

3

 

4

5

6

 

7

8

Содержимое

simbol

 

не используется

 

 

 

chislo

 

Величина выравнивания полей экземпляра структуры в оперативной памяти и как следствие общий размер структуры в памяти зависит от настроек компилятора. Для вышеприведѐнного случая sizeof(Test) вернѐт значение 8.

В тех случаях, когда заранее известно, что поля структуры одновременно использоваться не будут, можно использовать «объединение» (union). Объединение подобно структуре. Оно способно хранить в пределах одной зарезервированной области памяти различные типы данных. Но в каждый определенный момент времени в объединении хранится только один из этих типов данных и возможно использовать лишь значение этого поля. Синтаксически объединение определяется аналогично структуре:

129

union Test

{

char symbol; int chislo;

};

sizeof(Test) вернѐт значение 4 == sizeof(int) sizeof(char)

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

Ещѐ одна разновидность пользовательских типов - «перечисление». Перечисление по своей сути является набором констант, называемых перечислителями, например:

enum Season {Summer, Autumn, Winter, Spring};

Перечисления удобно использовать в конструкциях условий таких как

«if-else» или «switch-case»

Season season = Summer;

...

if (season == Summer)

{

// какой-то код

}

switch(season)

{

case Summer:

// какой-то код break;

case Autumn:

// какой-то код break;

// ...

}

Для работы со структурами удобно использовать функции. Например:

130

//Тип данных «целочисленный массив» struct Massiv

{

unsigned int quantity; // Количество данных int *data; // Указатель на данные

};

//Функция для создания массива

void CreateMassiv(Massiv& mass, unsigned int size)

{

mass.quantity = size; // Запоминаем размер mass.data = new int[mass.quantity]; // Выделяем память

// Заполняем созданный массив нулями for(unsigned int i = 0; i < mass.quantity; i++)

{

mass.data[i] = 0;

}

}

//Функция для удаления массива void DeleteMassiv(Massiv& mass)

{

delete[] mass.data;

}

//Функция для вывода элементов массива на экран void Vivod(const Massiv& mas)

{

for(unsigned int i = 0; i < mas.quantity; i++)

{

std::cout<<mas.data[i]<<"\t";

}

std::cout<<"\n\n";

}

//Теперь в основной программе можно записать: void main()

{

Massiv m; // Создаём структуру "Целочисленный массив" CreateMassiv(m, 5); // Выделяем память под массив из 5 элементов

m.data[0] = 3;

// Задаём значения некоторым элементам массива

m.data[1] = 5;

 

m.data[3] = 10;

 

Vivod(m);

// Вывод заполненного массива на экран

DeleteMassiv(m);

// Удаление массива

system("pause");

 

}

 

 

131

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

Операторы по своей сути так же являются функциями. От прочих функций они отличаются лишь тем, что имеют сокращѐнную форму записи. Например, записи a + b и operator+(a, b) эквивалентны.

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

struct MyVector

{

float x; float y; };

оператор «+» для сложения двух векторов можно перегрузить так:

// Глобальная перегрузка оператора «+»

MyVector operator+(const MyVector& A, const MyVector& B)

{

MyVector C;

C.x = A.x + B.x;

C.y = A.y + B.y; return C;

}

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

После реализации функции operator+ для «сложения» структур MyVector в тексте программы можно будет записать:

...

void main()

{

...

MyVector vect1; // Создаём один вектор vect1.x = 3;

132

vect1.y = 4;

MyVector vect2; // Создаём другой вектор vect2.x = 6.2;

vect2.y = -5;

// Создаём третий вектор, равный сумме двух предыдущих

MyVector vect3 = vect1 + vect2;

...

}

Использовать стандартный оператор «+» для сложения стандартных типов данных по-прежнему можно. Компилятор отличит его от вышеописанного по типу слагаемых (аргументов).

8.3Примеры решения задач

Задача А Создать тип данных для хранения целочисленного массива произвольной длины. Написать функции для заполнения массива случайными значениями и для вывода на экран всех значений массива. Перегрузить оператор «+» для поэлементного сложения двух целочисленных массивов.

Решение:

Алгорим работы функции для создания массива

Начало

Massiv mass unsigned int size

Получение mass и size

Записываем size (размер массива) в mass.quantity

Выделяем память под массив, указатель на неѐ записываем в mass.data

Обнуляем созданный массив (чтоб «мусора» не было)

Возвращение mass

Конец

133

Алгорим работы оператора "+" для сложения массивов

Начало

Massiv A, Massiv B

Получение A и B

Создать Massiv C, длиной не меньше чем A и чем B

Вычисляем суммы элементов A и B, стоящих на одинаковых позициях, пока не дойдѐм до конца одного из массивов; результаты заносим в C на те же позиции

Если один из массивов длинее другого, докопируем его элементы, не учавствовавшие в сложении в конец C

Возвращение С

Конец

Блок-схемы работы остальных функций здесь не приведены, они аналогичны и довольно просты.

Теперь составим программу:

#include <iostream> // Для использования объектов cin и cout для ввода-вывода

#include <time.h> // Для получения значения текущего времени для задания начального значения для генератора случайных чисел

//Тип данных «целочисленный массив» struct Massiv

{

unsigned int quantity; // Количество данных int *data; // Указатель на данные

};

//Функция для создания массива

void CreateMassiv(Massiv& mass, unsigned int size)

{

mass.quantity = size; // Запоминаем размер mass.data = new int[mass.quantity]; // Выделяем память

// Заполняем созданный массив нулями (избавляемся от «мусора») for(unsigned int i = 0; i < mass.quantity; i++)

{

mass.data[i] = 0;

}

134

}

//Функция для удаления массива void DeleteMassiv(Massiv& mass)

{

delete[] mass.data;

}

//Функция для заполнения массива случайными числами void Zapoln(Massiv& mas)

{

for(unsigned int i = 0; i < mas.quantity; i++)

{

mas.data[i] = rand() - RAND_MAX / 2;

}

}

//Функция для вывода элементов массива на экран void Vivod(const Massiv& mas)

{

for(unsigned int i = 0; i < mas.quantity; i++)

{

std::cout<<mas.data[i]<<"\t";

}

std::cout<<"\n\n";

}

//Перегрузка оператора "+" для сложения массивов

Massiv operator+(const Massiv& A, const Massiv& B)

{

//Создаём массив-результат, учитываем что складываемые массивы могут иметь разную длину

Massiv C;

C.quantity = A.quantity > B.quantity ? A.quantity : B.quantity; C.data = new int[C.quantity];

//Заносим в массив-результат суммы элементов массивов-аргументов const unsigned int temp = A.quantity < B.quantity ? A.quantity :

B.quantity;

for(unsigned int i = 0; i < temp; i++)

{

C.data[i] = A.data[i] + B.data[i];

}

// Если один из массивов длинее другого, докопируем оставшиеся элементы

const int *temppointer = A.quantity > B.quantity ? A.data : B.data; for(unsigned int i = temp; i < C.quantity; i++)

{

C.data[i] = temppointer[i];

}

135

return C;

}

// Для проверки работоспособности созданных функций напишем: void main()

{

srand(time(NULL));

// Создаём два массива

Massiv mass1; CreateMassiv(mass1, 5); Zapoln(mass1); Vivod(mass1);

Massiv mass2;

CreateMassiv(mass2, 3);

Zapoln(mass2);

Vivod(mass2);

//Суммируем их в третий

Massiv mass3 = mass1 + mass2;

//Выводим результат на экран

Vivod(mass3);

//Освобождаем память

DeleteMassiv(mass1);

DeleteMassiv(mass2);

DeleteMassiv(mass3);

system("pause");

}

Тесты

Проведѐм несколько тестов созданных функций. В функцию для создания массива передаѐм указатель на экземпляр структуры «Massiv» и его размер. В остальные функции передаѐм только указатель на экземпляр структуры «Massiv», размер массива уже записан в еѐ поле «quantity».

Размер: 5

10060

5242

5480

-10265 9346

Размер: 3

5786

15364

3682

 

 

Сумма:

15846

20606

9162

-10265 9346

Размер: 5

11285

5431

-12895 2278

10688

Размер: 5

3750

2885

-12702 -5811

-457

Сумма:

15035

8316

-25597 -3533

10231

 

 

 

 

 

136

Размер: 6

11517

14904

10277

6837

-10641 -12364

 

 

Размер: 9

-5507

8838

-13136 -10986

-4573 -2199 -9022

-4629

-5947

Сумма:

6010

23742

-2859

-4149

-15214 -14563 -9022

-4629

-5947

Массивы заполняются случайными значениями. Суммы массивов посчитаны правильно. Программа работает верно.

8.4Задания:

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

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

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

Задача 7.1.1. Создать тип данных для хранения информации о книге: название, автор, издательство, год издания, количество страниц. Написать функции для поиска в массиве книг и вывода на экран всех книг заданного автора, заданного издательства.

Задача 7.1.2. Создать тип данных для хранения информации о книге: название, автор, издательство, год издания, количество страниц. Написать функции для поиска в массиве книг и вывода на экран книг, изданных в заданном промежутке времени.

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

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

137