Партнерка на США и Канаду по недвижимости, выплаты в крипто
- 30% recurring commission
- Выплаты в USDT
- Вывод каждую неделю
- Комиссия до 5 лет за каждого referral
Лекция 13
Структуры и объединения
Структура – это объединенное в единое целое множество поименованных элементов разных типов.
В отличие от массива, все элементы которого однотипны, структура может содержать элементы разных типов. Если провести аналогию с языком Паскаль, то структура – это аналог записи. Структуры позволяют организовывать базы данных на языке С++. Элементы структуры называются полями структуры и могут иметь любой тип, кроме типа этой же структуры, но могут быть указателями на него.
struct [ имя_типа ] {тип_1 элемент_1;тип_2 элемент_2;тип_n элемент_n;}
[список_описателей];
Если отсутствует имя типа, должен быть указан список описателей переменных, указателей или массивов. В этом случае описание структуры служит определением элементов списка:
struct
{
string fio[30];
int code;
float oklad;
} stuff[100], *ps; /*определение массива структур и указателя на структуру */
Если список отсутствует, описание структуры определяет новый тип, имя которого можно использовать в дальнейшем наряду со стандартными типами, например:
struct Worker
{ //описание нового типа Worker
string fio;
int code;
float oklad;
}; //описание заканчивается точкой с запятой
Worker stuff[100], *ps; /* определение массива типа Worker */
/* и указателя на тип Worker */
Для инициализации структуры значения ее элементов перечисляют в фигурных скобках в порядке их описания:
Struct
{
string fio;
int code;
float oklad;
}
worker = {"Страусенко", 215, 34001.55};
Для переменных одного и того же структурного типа определена операция присваивания, при этом происходит поэлементное копирование.
Структуру можно передавать в функцию и возвращать в качестве значения функции. Другие операции со структурами могут быть определены пользователем.
Доступ к отдельным полям структуры выполняется с помощью операций выбора. (точка) при обращении к полю через имя структуры и -> при обращении через указатель, например:
Worker worker, stuff[100], *ps;
...
worker. fio = "Страусенко";
stuff[8].code = 215;
ps->salary = 0.12;
Ввод и вывод информации при работе со структурами осуществляется поэлементно.
Пример
#include <iostream>
#include <string>
using namespace std;
int main()
{
struct Students
{
unsigned int num;
string fio;
unsigned short int kurs;
};
Students stud[5];
for (int i = 0; i < 5; i++) {
cout << "Please enter information about the student " << i << '\n';
cout << "Please enter the number of the student : " << '\n';
cin >> stud[i].num;
cout << "Please enter the name of the student : " << '\n';
cin >> stud[i].fio;
cout << "Please enter the kurs of the student : " << '\n';
cin >> stud[i].kurs;
}
for (int i = 0; i < 5; ++i) {
cout << stud[i].num <<" "<< stud[i].fio<<" " << stud[i].kurs<<'\n';
}
}
Битовые поля
В структуре можно определить размеры атрибута с точностью до бита. Традиционно структуры используются в системном программировании для описания регистров аппаратуры. В них каждый бит имеет свое значение. Не менее важной является возможность экономии памяти — ведь минимальный тип атрибута структуры это байт (char), который занимает 8 битов. До сих пор, несмотря на мегабайты и даже гигабайты оперативной памяти, используемые в современных компьютерах, существует немало задач, где каждый бит на счету.
Если после описания атрибута структуры поставить двоеточие и затем целое число, то это число задает количество битов, выделенных под данный атрибут структуры. Такие атрибуты называют битовыми полями. Следующая структура хранит в компактной форме дату и время дня с точностью до секунды.
struct TimeAndDate
{
unsigned hours :5; // часы от 0 до 24
unsigned mins :6; // минуты
unsigned secs :6; // секунды от 0 до 60
unsigned weekDay :3; // день недели
unsigned monthDay :5; // день месяца от 1 до 31
unsigned month :4; // месяц от 1 до 12
unsigned year :12 // год от 0 до 4096
};
Бинарные файлы
По способу доступа файлы делятся на последовательные и файлы с произвольным доступом. Файлы с последовательным доступом – файлы, в которых чтение и запись производятся сначала, байт за байтом. К такому типу файлов относятся рассмотренные ранее текстовые файлы. Но поиск нужной информации в файлах такого типа может занять очень много времени, так как, например, чтобы добраться до 1000-го элемента файла, надо прочитать предыдущие 999 элементов.
Файлы с произвольным доступом допускают чтение и запись в произвольной позиции. К файлам с произвольным доступом относятся бинарные (двоичные) файлы. Чтобы открыть файл в бинарном виде, необходимо указать режим ios::binary.
Например, если мы хотим открыть двоичный файл для записи, то можно записать так:
f. open("a. dat",ios::out|ios::binary);
При работе со структурами можно записывать их в текстовый файл поэлементно, а можно использовать двоичные файлы. В этом случае записывать в файл, а также считывать из файла нужно сразу всю структуру. Для считывания и записи структур необходимо использовать функции read и write. Чтобы записать структуру в файл, нужно сообщить компилятору адрес структуры, приведенный к типу “указатель на char” и размер записываемой структуры, то есть синтаксис команды будет следующий (record – это имя переменной структурного типа):
f. read((char *)&record, sizeof(record));
f. write((char *)&record, sizeof(record));
Запишем приведенную выше программу с использованием бинарных файлов:
#include <iostream>
#include <string>
#include <fstream>
using namespace std;
int main()
{
struct Students
{
unsigned int num;
string fio;
unsigned short int kurs;
};
fstream f;
f. open("a. dat",ios::out|ios::binary);
// как и с обычными типами, вы можете объявить массив структур
Students stud;
for (int i = 1; i <= 5; i++) {
cout << "Please enter information about the student " << i << '\n';
cout << "Please enter the number of the student : " << '\n';
cin >> stud. num;
cout << "Please enter the name of the student : " << '\n';
cin >> stud. fio;
cout << "Please enter the kurs of the student : " << '\n';
cin >> stud. kurs;
f. write((char *)&stud, sizeof(stud));
}
f. close();
f. open("a. dat",ios::in|ios::binary);
for (int i = 1; i <= 5; ++i) {
f. read((char *)&stud, sizeof(stud));
cout << stud. num <<" "<< stud. fio<<" " << stud. kurs<<'\n';
}
f. close();
}
Каждый файл имеет два связанных с ним значения: указатель чтения и указатель записи, по-другому называемые файловым указателем или текущей позицией. При последовательном доступе к элементам файлов перемещение файлового указателя происходит автоматически. Благодаря наличию файлового указателя, в двоичных файлах допустим произвольный доступ к их элементам.
seekg(streamoff, ios::seek_dir) переместить текущий указатель чтения на заданное количество байтов;
seekp(streamoff, ios::seek_dir) – переместить текущий указатель записи на заданное количество байтов;
Первый из параметров определяет, на сколько байтов необходимо сместить указатель, второй – откуда осуществлять поиск
Значения второго параметра:
Beg (0) - поиск от начала файла
Cur (1) - поиск от текущей позиции файла
End(2) - поиск от конца файла
Предположим, что в рассмотренном выше примере, необходимо вывести не полный список студентов, а только запись с указанным номером.
Students stud[5],s;
...
f. open("a. dat",ios::in|ios::binary);
int i;
cout<<"Number of record"<<'\n';
cin>>i;
f. seekg(sizeof(Students)*(i-1),ios::beg);
f. read((char *)&s, sizeof(s));
cout << s. num <<" "<< s. fio<<" " << s. kurs<<'\n';
f. close();
Бинарные файлы можно использовать для хранения данных любых типов.
Пример. Ввести в файл прямого доступа 5 чисел, а затем переписать их в другой файл в обратном порядке.
#include <iostream>
#include <fstream>
using namespace std;
int main()
{
int a;
fstream f, f1;
f. open("a. dat",ios::out|ios::binary);
f1.open("b. dat",ios::out|ios::binary);
// как и с обычными типами, вы можете объявить массив структур
for (int i = 0; i < 5; i++) {
cin >> a;
f. write((char *)&a, sizeof(a));
}
f. close();
f. open("a. dat",ios::in|ios::binary);
for (int i = 4; i >=0; i--) {
f. seekg(sizeof(a)*i, ios::beg);
f. read((char *)&a, sizeof(a));
cout << a ;
f1.write((char *)&a, sizeof(a));
}
f. close();
f1.close();
}
Объединения (union)
Программе может потребоваться работать с двумя или несколькими значениями, используя при этом только одно значение в каждый момент времени. В таких случаях для хранения данных ваши программы могут использовать объединения.
Объединения C++ очень похожи на структуры, за исключением того, как C++ хранит их в памяти; кроме того, объединение может хранить значение только для одного элемента в каждый момент времени.
Объединение представляет собой структуру данных, подобную структуре C++, и состоит из частей, называемых элементами.
Объединения очень похожи на структуры C++, однако способ, с помощью которого C++ хранит объединения, отличается от способа, с помощью которого C++ хранит структуры.
Внутри ваших программ объединения C++ очень похожи на структуры. Например, следующая структура определяет объединение с именем distance, содержащее два элемента:
union distance
{
int miles;
long meters;
};
Как и в случае со структурой, описание объединения не распределяет память. Вместо этого описание предоставляет шаблон для будущего объявления переменных.
union distance
{
int miles;
long meters;
};
distance japan, germany, franсe;
Данное объединение содержит два элемента: miles и meters. Эти объявления создают переменные, которые позволяют вам хранить расстояния до указанных стран. Как и для структуры, ваша программа может присвоить значение любому элементу. Однако в отличие от структуры значение может быть присвоено только одному элементу в каждый момент времени. Когда вы объявляете объединение, компилятор C++ распределяет память для хранения самого большого элемента объединения. В случае объединения distance компилятор распределяет достаточно памяти для хранения значения типа long.
Предположим, что ваша программа присваивает значение элементу miles, как показано ниже:
japan. miles = 12123;
Если далее ваша программа присваивает значение элементу meters, значение, присвоенное элементу miles, теряется.
#include <iostream>
using namespace std;
void main(void)
{
union distance
{
int miles;
long meters;
} walk;
walk. miles = 5;
cout << "Пройденное расстояние в милях " << walk. miles ;
walk. meters = 10000;
cout << "Пройденное расстояние в метрах " << walk. meters << endl;
cout << "Пройденное расстояние в милях " << walk. miles << endl;
}
Объединение хранит значение только одного элемента в каждый момент времени
Объединение представляет собой структуру данных, которая, подобно структуре C++, позволяет вашим программам хранить связанные части информации внутри одной переменной. Однако в отличие от структуры объединение хранит значение только одного элемента в каждый момент времени. Другими словами, когда вы присваиваете значение элементу объединения, вы перезаписываете любое предыдущее присваивание.
Объединение определяет шаблон, с помощью которого ваши программы могут позднее объявлять переменные. Когда компилятор C++ встречает определение объединения, он распределяет количество памяти, достаточное для хранения только самого большого элемента объединения.
Перечисления (перечисляемый тип)
При написании программ часто возникает потребность определить несколько именованных констант, для которых требуется, чтобы все они имели различные значения. Для этого удобно воспользоваться перечисляемым типом данных. Формат:
enum [ имя_типа ] { список_констант };
Имя типа задается в том случае, если в программе требуется определять переменные этого типа. Компилятор обеспечивает, чтобы эти переменные принимали значения только из списка констант.
Константы должны быть целочисленными и могут инициализироваться обычным образом. При отсутствии инициализатора первая константа обнуляется, а каждой следующей присваивается на 1 большее значение, чем предыдущей:
enum Err { ERR_READ, ERR_WRITE, ERR_CONVERT};
Err error;
...
switch (error)
{ case ERR_READ: /* операторы */ break;
case ERR_WRITE: /* операторы */ break;
case ERR_CONVERT: /* операторы */ break;}
Константам ERR_READ, ERR_WRITE, ERR_CONVERT присваиваются значения 0, 1 и 2 соответственно.
Возможно непосредственное присваивание константам значений. Например:
enum Err { ERR_READ=1, ERR_WRITE, ERR_CONVERT=4};
В данном случае первая константа будет равна 1, вторая - 2, третья – 4.
Использование перечисляемого типа позволяет, например, отследить выход за пределы границ значений. Попытка присвоить переменной данного типа значение, отличное от одного из элементов перечисления (или передать его параметром в функцию), вызовет ошибку компиляции. Перечисляемый тип делает программу более наглядной и читаемой. При попытке вывода переменной перечисляемого типа выводиться будет только целочисленное значени.
Пример
#include <iostream>
using namespace std;
//определяем перечисление
enum level {parking, supermarket, hardwareStores, boutiques, sportSpa, clubRestaurantBar};
int main()
{
setlocale(LC_ALL, "rus");
level floor = parking;//переменная типа перечисления level
//level floor = 0; не скомпилируется
int fl = floor;//выбор этажа пользователем
bool exit = true;//выбор пользователя - выйти и продолжить
cout << "\n\t$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$\n\n";
cout << "\tДобро пожаловать в наш торгово-развлекательный центр MALL!!!\n";
cout << "\tПредлагаем Вам проехаться в лифте и посетить все этажи!\n\n";
cout << "\t$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$\n\n";
while(exit)//пока exit равен true
{
cout << "\nНажмите кнопку с номером этажа (от 0 до 5): ";
cin >> fl;
switch(fl)
{
case(parking):
cout << "\aВы спустились в паркинг!!!" << endl;
break;
case(supermarket):
cout << "\aВы на первом этаже!";
cout << "\nЗдесь вы можете посетить наш супермаркет и купить продукты и товары для дома.\n\n";
break;
case(hardwareStores):
cout << "\aВы на втором этаже!";
cout << "\nЗдесь расположились магазины бытовой техники, IT и мобильных телефонов.\n\n";
break;
case(boutiques):
cout << "\aВы на третьем этаже!";
cout << "\nЗдесь вас ждет незабываемый шопинг! Одежда, обувь, магазины косметики.\n\n";
break;
case(sportSpa):
cout << "\aВы на четвертом этаже!";
cout << "\nЗдесь вы можете посетить бассейн, каток, спортзалы, spa-салон!\n\n";
break;
case(clubRestaurantBar):
cout << "\aВы на пятом этаже!";
cout << "\nТут вы можете посетить ночной клуб, бар и ресторан!\n\n";
break;
default: cout << "\a\a\aОшибка! У нас только 5 этажей!\n\n";
}
cout << "Если хотите выйти на этом этаже, нажмите 0.\n";
cout << "Продолжить увлекательную поездку - нажмите 1: ";
cin >> exit;
}
return 0;
}


