МУ_ЛР6_ОП (Методические указания к лабораторным работам), страница 2
Описание файла
Файл "МУ_ЛР6_ОП" внутри архива находится в папке "Методические указания к лабораторным работам". Документ из архива "Методические указания к лабораторным работам", который расположен в категории "". Всё это находится в предмете "программирование на основе классов и шаблонов" из 2 семестр, которые можно найти в файловом архиве МГТУ им. Н.Э.Баумана. Не смотря на прямую связь этого архива с МГТУ им. Н.Э.Баумана, его также можно найти и в других разделах. Архив можно найти в разделе "книги и методические указания", в предмете "программирование на основе классов и шаблонов" в общих файлах.
Онлайн просмотр документа "МУ_ЛР6_ОП"
Текст 2 страницы из документа "МУ_ЛР6_ОП"
Face Face2;
…
Face2.birthdate.day = 30; // Двойная квалификация
…
9 Указатели на структуры и на динамические структуры
Мы уже говорили, что на структуру можно задать указатель. Пусть есть такое описание указателя:
struct Person { // Персона
char name[50];
Date birthdate; // структурная переменная дата рожденич
double salary; // Оклад
};
…
Person p2 = { "Сидоров", {10, 3, 1978}, 1500.48 };
…
Person * pPerson = &p2;
…
// Изменить оклад
pPerson -> salary = 10.50;
(*pPerson).salary = 12.50; // Можно и так, но скобки обязательны
Ниже для вложенных структурных переменных мы увидим, в которых объявлены указатели на другие структуры, мы увидим, что допустима запись и такого вида:
ptrPerson -> pStudent -> salary = 15.00;
и такого вида (где имеет место двойная квалифицированная ссылка):
p2.birthdate. year = 2014;
10 Передача структур в функцию
Передать структуру в функцию, в качестве фактического параметра можно двумя способами: по значению, тогда изменить структуру в функции нельзя, и передать указатель на структуру. Рассмотрим первый случай. Пусть есть функция для печати поля структуры:
//
// Функция печати
void PrintPersonName( Person per)
{
printf ("Печать в функции per.name = %s\n" , per.name);
per.salary = 5.0;
};
…
Прототип в главной программе
void PrintPersonName( Person per);
…
В программе описания и вызов:
// Описание структуры
Person p2 = { "Сидоров", {10, 3, 1978}, 1500.48 };
//Вызов функции параметром структура
printf( "До функции p2.salary = %f \n" ,p2 .salary );
PrintPersonName( p2 ); // p2
printf( "После функции p2.salary = %f \n" ,p2 .salary );
Получим результат:
До функции p2.salary = 15.000000
Печать в функции per.name = Сидоров
После функции p2.salary = 15.000000
Значение полей структуры (в частности p2.salary) не изменяются.
11 Передача указателя на структуру в функции
Если мы хотим изменить значения полей в структурной переменной, то необходимо передать в нее указатель на нее. Для функции:
//
void ChangePersonSalary( Person * p , double newSalary)
{
p -> salary = newSalary; // Изменение по указателю
};
…
Прототип в главной программе
void PrintPersonName( Person per);
…
// Описание структуры
Person p2 = { "Сидоров", {10, 3, 1978}, 15.00 };
…
// Передача указателя
printf( "До функции p2.salary = %f \n" ,p2 .salary );
ChangePersonSalary( &p2 , 30.0); // Передача указателя = &p2
printf( "После функции ChangePersonSalary p2.salary = %f \n" ,p2 .salary );
Получим результат:
До функции p2.salary = 15.000000
После функции ChangePersonSalary p2 .salary = 30.000000
12 Массивы структур
Так как структура определяет новый тип переменной, то разрешается описывать массивы структур. Например:
// Описание массива структур типа Prepod
Prepod KafIU[30];
// Работа с элементами массива структур
KafIU[0].Oklad = 10.0;
KafIU[10].Oklad = 10.0;
// Цикл занесения
for (int i =0 ; i < 30 ; i++ )
{
KafIU[i].Oklad = 10.0;
};
Можно с массивом работать через указатель (ptrMas), при этом инициализация его должна быть проведена с начальным адресом массива структур (&KafIU[0]) или именем этого массива (KafIU), так как имя массива задает адрес начала массива.
// или с указателем
//Prepod *ptrMas = &KafIU[0]; // или
Prepod *ptrMas = KafIU;
Возможны способы: индексации указателя (ptrMas[2].Oklad), или вычислением нового адресного выражения для адреса ( ptrMas + 2) или выбором конкретного элемента массива – структуры посредством операции разыменования (*). Смотрите ниже примеры:
ptrMas[2].Oklad = 15.0; // для второго элемента массива структур
( ptrMas + 2) -> Oklad = 25.0; // можно и так
(*( ptrMas + 2)).Oklad = 35.0; // можно и так
ptrMas = ptrMas + 2;
ptrMas->Oklad = 45.0; // можно и так
(*ptrMas).Oklad = 55.0; // можно и так
Во всех рассмотренных случаях будет изменена одно и тоже поле элемента структуры с номером 2.
13 Вложенные структуры
Внутри одной структуры может быть описана другая структура (тип Prepod - DecPrep) или указатель (тип Person - pStudent).
// Структура со вложенными указателями
struct Person {
char name[50];
Date birthdate; // структурная переменная дата рожденич
double salary; // Оклад
};
//
struct Prepod {
char fam[50]; // Фамилия
Person * pStudent; // Указатель на структурную переменную Person
double Oklad; // Оклад
};
// Структура со вложенными структурами
struct Decan {
char fam[50]; // Фамилия
Prepod DecPrep; // Cтруктурная переменная Prepod вложенная
double Oklad; // Оклад
};
В программе для указателей поместим операторы вычисления (salary) оклада:
Prepod *ptrPerson = (Prepod * ) malloc ( sizeof (Prepod));
ptrPerson ->pStudent = &p2;
ptrPerson ->pStudent ->salary = 15.00;
// Доступ возможет разными способами
printf( "p2 .salary = %f \n" ,p2 .salary );
printf( "ptrPerson ->pStudent ->salary = %f \n" ,ptrPerson ->pStudent ->salary );
В результате получим:
p2 .salary = 15.000000
ptrPerson ->pStudent ->salary = 15.000000
Для вложенных структур (в структуру Decan вложена структура Prepod, см. Описания выше) можем записать, включая доступ и посредством указателя (pStudent->salary):
// Вложенные структуры
Decan DecIU;
DecIU.DecPrep.Oklad = 100.00;
// Доступ с указателем
DecIU.DecPrep.pStudent = &p2;
DecIU.DecPrep.pStudent->salary = 10.0;
14 Размер и размещение структур в ОП
Поля структуры располагаются в оперативной памяти последовательно, в связи с описанием в программе. При размещении в памяти разные типы должны быть выровнены на границу адреса своего размера ( int – 2 байта, long – 4 байта, double – 8 байт и т.д.). Из-за этого, даже при равных по количеству и типу полей размер структуры может отличаться. Это показано на примере структур First и Second.
struct First {
int i;
long j;
double k;
};
struct Second {
int i;
double k;
long j;
};
В программе определим актуальный размер структуры:
// Размещение структур в ОП
printf( "Размер структуры First = %d \n" ,sizeof (First) );
printf( "Размер структуры Second = %d \n" ,sizeof (Second) );
…
В результате получим:
Размер структуры First = 16
Размер структуры Second = 24
15 Динамической структуры
Работа с динамическими структурами и их массивами выполняется посредством указателей. Выделение памяти производиться библиотечными функциями из malloc.h. В этой библиотеке доступны функции: malloc, calloc, free, realolc и др. На примере, размещенном ниже, показано применение этих функций для структур.
Decan * pDec = (Decan * ) malloc ( sizeof (Decan)); // Выделение динамической памяти
pDec ->DecPrep.Oklad = 100.00; // Работа с полямиуктур
strcpy( pDec ->fam , "Фамилия декана");
pDec ->Oklad = 50.00;
pDec = (Decan *) realloc( pDec , sizeof (Decan) * 2 ); // Изменение размера
//
pDec = pDec + 1;
strcpy( pDec ->fam , "Новая Фамилия декана");
pDec = pDec - 1; // Востановление указателя для освобождения памяти (можно и /запомпить)
free ( pDec );
Обратите внимание на использование функции realloc, позволяющей изменить размер выделенной памяти в два раза, а также добавление к указателю 1-цы. При операциях целого типа с указателями определенного вида 1-ца соответствует размеру типа, для которого объявлен данный указатель.
16 Создание и удаление динамической структуры со строками
В динамических структурах часто возникает задача работы со строками (символьными массивами). Если заранее в структуре выделять максимальное число требуемых знаков в символьных массивах для строк (так мы поступали ранее, см. – fam , name), то, очевидно, будет значительный перерасход памяти. Поэтому при инициализации таких структур экономнее выделить столько памяти, сколько необходимо, то есть динамической памяти. Аналогичная процедура необходима и для изменения значений строковых полей: сначала идет освобождение (free), а затем новый захват ( malloc). Покажем это на примере. Пусть есть структура, в которой два указателя на строки (pName и pAvtor):
struct Book{
char * pName; // Название книги
char * pAvtor; // Автор книги
int StrCount; // Число страниц в книге
};
При ее инициализации нужно захватить память и записать строку:
Book Book1;
char * pStr = (char *) malloc ( strlen ("Три мушкетера") + 1);
Book1.pName = pStr;
strcpy(pStr , "Три мушкетера" );
pStr = (char *) malloc ( strlen ("Александр Дюма") + 1);
Book1.pAvtor = pStr;
strcpy(pStr , "Александр Дюма" );
Book1.StrCount =670;
При завершении программы динамическая память должна быть освобождена:
// При завершении программы освободим память
free (Book1.pName);
free (Book1.pAvtor);
//
Если вся структура динамически порождается, текст программы будет выглядеть так:
// Динамическая структура
Book * pBook = (Book *) malloc ( sizeof(Book));
pStr = (char *) malloc ( strlen ("Две Дианы") + 1);
pBook->pName = pStr;
strcpy(pStr , "Две Дианы" );
pStr = (char *) malloc ( strlen ("Александр Дюма") + 1);
pBook->pAvtor = pStr;
strcpy(pStr , "Александр Дюма" );
pBook->StrCount =470;
// При завершении программы освободим память под строки и саму структуру
free (pBook->pName);
free (pBook->pAvtor);
free (pBook );
//
Если нужно изменить отдельную строку, то память предварительно освобождаем:
free (pBook->pName);
pStr = (char *) malloc ( strlen ("Дама с камелиями") + 1);
pBook->pName = pStr;
strcpy(pStr , " Дама с камелиями" );
17 Структуры со ссылками на себя
В структурах нельзя использовать поля - структурные переменные такого же типа, однако поля - указатели на эти структуры допускаются. Например, элемент двухсвязного списка может выглядеть так:
// структуры со ссылками на самих себя
struct Node {
Node * pNext;
Node * pPrev;
int ValList;
};
В данном случае pNext и pPrev являются указателями на структуру Node.
18 Перечисления - enum
Это набор именованных целых констант, используемый для большей наглядности программы и ее переносимости. Перечисление может быть использовано для объявления переменных, которые принимают значения на заданном множестве значений. Примеры для перечислений: дни недели, месяцы в году, времена года, типы переменных и т.д. Формально перечисления (enum) могут быть описаны так:
enum [ <имя> ] {список перечисления} [ список переменных];
Например, для дня недели можно задать список констант:
// Дни недели
enum { mon , tue , wed , thu , fri , sat , sun };
int d = mon; // mon = 0 , tue = 1 и т.д.
Можно задать имя для объявления переменных перечисления:
// Дни недели с именем day
enum day { mon , tue , wed , thu , fri , sat , sun };