Структура — це сукупність різнотипних елементів, яким присвоюється одне ім’я (воно може бути відсутнім), що займає одну ділянку пам’яті. Елементи, що складають структуру, називаються полями.
Змінна типу структура, як і будь-яка змінна, повинна бути описана. Цей опис складається з двох кроків: опису шаблону (тобто складу) або типу структури та опису змінних структурного типу.
Синтаксис опису структури має вигляд:
struct [<ім’я структури>] { <тип 1> ім’я поля 1; <тип 2> ім’я поля 2 . . .; } р1, р2 . . .; |
де struct — службове слово;
<ім’я структури> — ім’я типу структура (може бути відсутнім);
<тип 1>, <тип 2> — імена стандартних або визначених типів;
ім’я поля 1, ім’я поля 2,… — імена полів структури;
р1, р2 . . .; — імена змінних типу структура.
Наприклад, для знаходження середнього бала, отриманого студентами в період сесії з дисциплін «Математика», «Фізика» та «Програмування», визначимо таку структуру:
struct stud { char fam [25]; // фамилия и инициалы int mat, fiz, prg; // предметы float sb; // средний балл } st1, st2;
Змінні st1 і st2 можна оголосити окремим оператором, наприклад:
struc stud stl st2;.
Ініціювання полів структури слід здійснювати або при її описі, або в тілі програми. При описі структури ініціювання полів виглядає, наприклад, так:
struct stud { char fam [25]; int mat,fiz, prg; float sb; } st1 = {"Кравченко И. С.", 4, 5, 5}; st2 = {"Тесленко А. М.", 3, 4, 5};
Якщо ініціювання виконується в тілі програми, то для звернення до імені поля треба спочатку записати ім’я структурної змінної, а потім ім’я поля. Ці обидва записи відокремлюються крапкою і являють собою складене ім’я.
Отже, у випадку появи змінної st1 у програмі для її ініціювання можна записати stud st1 = {“Кравченко И. С.”, 4, 5, 5}; або ініціювання виконується за допомогою складених полів. Розглянемо ілюстраційну програму:
#include <iostream.h> #include <string.h> #include <stdio.h> #include <conio.h> main ( ) { struct stud //-------------------- описание структуры { char fam [20]; int mat, fiz, prg; float sb; } st1, st2; strcpy (st1.fam, "Кравченко И. С."); st1 .mat = 4; st1 .fiz = 5; st1 .prg = 5; st1.sb = float (st1.fiz + st1.mat + st1.prg)/3; st2 = st1; puts (st2.fam); //---------------- вывод фамилии cout << st2.mat << st2.fiz << st2.prg << st2.sb << endl; getch(); //задержка экрана вывода результата }
У наведеній програмі організується присвоювання всім полям структури st1 відповідних значень. Слід зауважити, що поле st1.fam одержує значення шляхом застосування функції strcpy (st1.fam, “Кравченко И. С.”);. Структурна змінна st2 того ж типу, що і st1, тому справедлива операція st2 = st1;.
Якщо функція використовує тільки один структурний тип, то цей тип можна оголосити без імені. Тоді раніше розглянуту структуру можна оголосити таким чином:
struct { char fam [25]; int mat, fiz, prg; float sb; } stl, st2;
Коли при описі структур у деякій функції або в межах видимості змінних у різних функціях є багато (але не всі) однакових полів, то їх слід об’єднати в окрему структуру. Її можна застосовувати при описі інших структур, тобто поля структури можуть самі бути типу struct. Це називається вкладеністю структур — її можна використати, наприклад, якщо треба обробляти списки студентів та викладачів університету. Студентські списки містять дані: прізвище та ініціали, дата (день, місяць, рік) народження, група та середній бал успішності, а в списках викладачів присутні такі дані: прізвище, ініціали, дата народження, кафедра, посада. У процесі обробки списку студентів і списку викладачів можна оголосити відповідно такі структури:
struct stud
{ char fio [25];
int den, god;
char mes [10];
char grup;
flout sb; }
struct prep
{ char fio [25];
int den, god;
char mes [10];
char kaf, dolg;
}
В оголошених типах однакові поля можна об’єднати в окрему структуру і застосовувати її при описі інших типів. Поетапно це виглядає так:
- загальна структура —
struct spd
{ char fio [25];
int den, god;
char mas[10]; }
- структура для опису інформації про студентів —
struct stud
{ spd dr;
char grup;
float sb}
st1, st2;
- структура для опису інформації про викладачів —
struct prep
{ spd dr;
char kaf [10];
char dolg [15];
} pr1, pr2;
У структурах stud і prep для оголошення поля, що містить дані про прізвище і дату народження, використовується раніше описаний тип spd. Тепер до поля fio, den, god, mes можна звернутися, використовуючи запис st1.dr.fio, наприклад, при зверненні до функції введення:
gets (st1.dr.fio);
або gets (pr1.dr.fio);.
Після оголошення структурного типу змінних для роботи з їхніми полями можна застосовувати і покажчики, тоді опис структури матиме вигляд:
struct stud
{ char fam [25];
int mat, fiz, prg;
float sb;
} st1, *pst;
Доступ до полів може здійснюватися двома способами:
- з використанням операції розіменування «*», тобто
gets ((*pst).fam); (*pst).fiz = 5;
- з використанням покажчика *->*, наприклад,
gets (pst -> fam); pst-> fiz = 5; тощо.
Крім того, до полів змінної st1 можна звертатися, вказуючи поля через операцію «.», як це робилося раніше.
Дані типу структура можна об’єднати в масиви, тоді для розглянутого вище ілюстраційного прикладу з урахуванням кількості студентів, структуру можна записати так:
struct stud
{ char fam [20];
int mat, fiz, prg;
float sb;
} spis[15], *sp = &spis[0];.
У випадку, коли масив описується десь у тексті програми, тобто не саме після опису структури, його можна оголосити у вигляді: stud spis [15]; — масив типу структура з ім’ям stud, що містить відповідну інформацію про групу із 15 студентів.
Доступ до елементів масиву типу структура здійснюється із застосуванням індексу або через покажчик-константу, яким є ім’я масиву, тобто одним з таких способів:
strcpy (spis[1].fam, ” “);
spis[1].fiz = 5;
або
strcpy ((sp + 1) -> fam, ” “);
(sp + 1) -> fiz = 5;
або
strcpy ((*(spis + 1)).fam, ” “);
(*(sp + 1)).fiz = 5;.
У останньому виразі (*(spis + 1)).fiz = 5; потрібна зовнішня пара дужок, тому що операція «.» («крапка») має пріоритет вище, ніж операція розіменування «*».
Поля структури можуть також бути масивами. Наприклад, у розглянутій структурі stud можна оцінки з різних предметів об’єднати в масив. Тоді структуру слід описати у вигляді:
struct stud1
{ char fam [25];
int pred [3];
float sb
} spis[15], *ps = &spis[0];.
Звернення до полів здійснюватиметься одним із способів:
((*ps).fam)
(ps->pred [0]),
наприклад,
gets ((*ps).fam);
сіn >> ps -> pred[0] >> ps -> pred[1] >> ps -> pred[2];
або сіn >> ps -> *(pred + 0) >> ps -> *(pred + 1) >> ps -> *(pred + 2).
Доцільно також зазначити, що бібліотека stdlib.h містить спеціальні функції для пошуку та сортування структурних змінних.
Розглянемо використання структурного типу на прикладах.
Приклад 8.1. Увести в комп’ютер відомість успішності групи студентів, які здали сесію з дисциплін «Математика», «Фізика» і «Програмування», та обчислити:
- середній бал кожного студента;
- середній бал групи за кожним предметом;
- вивести на екран прізвища відмінників з програмування.
Розглянемо перший варіант (Р8_1_1.СРР) реалізації поставленої задачі, що може мати вигляд:
// Р8_1_ 1.СРР — обработка ведомости успеваемости группы студентов //------------------------------- использование данных типа структура #include <iostream.h> #include <string.h> #include <conio.h> void main() { const n = 5; //n — количество студентов int і; float sm, sf, sp; /* sm, sf.sp — сумма оценок группы соответственно по математике, физике, программированию */ struct stud { char fam[25]; int mat, fiz, prg; float sb; } ved[n]; //ved[n] — массив студентов (ведомость) sm = sf = sp = 0; //---- ввод и обработка информации о студентах и их успеваемости for (і = 0; і < n; i++) { cout <<"***** Введите информацию о "<< (i+1) << " студенте\n"; { cout<<<<"Введите фамилию и инициалы\n"; gets(ved[i]. fam); cout <<"Оценки по матем., физике и программир.\n"; cin >> ved[i].mat >> ved[i].fiz >> ved[i].prg;//--------------------- вычисление среднего балла студента за сессию ved[i].sb = (float(ved[i].mat + ved[i].fiz + ved[i].prg))/3;//--------------------- суммирование оценок в группе по предметам sm += ved[i].mat; sf += ved[i].fiz; sp += ved[i].prg; } } //--------------------- вывод результатов вычислений cout << ''\n******* Результаты сессии\n"; cout.precision(3); for (і = 0; і < n; i++) cout<<i+1<< " "<< ved[i].fam << " матем. = " << ved[i].mat << " физика = "<< ved[i].fiz << "програм. = " <<ved[i].prg << " ср. балл = " << ved[i].sb << "\n"; cout << "\n\nСредний балл группы по математике = "<< sm/n; cout << "\nСредний балл группы по физике = "<< sf/n; cout << "\nСредний балл группы по программированию = "<<sp/n; cout << "\n\n***** Отличники по программированию: \n"; for (і = 0; і < n; i++) if (ved[i].prg == 5) cout << ved[i].fam <<"\n"; getch (); }
Результати виконання програми:
***** Введите информацию о 1 студенте
Введите фамилию и инициалы
Адаменко Р. Л.
Оценки по матем., физике и программир.
4 4 5
***** Введите информацию о 2 студенте
Введите фамилию и инициалы
Бочаренко П. П.
Оценки но матем., физике и программир.
5 5 4
***** Введите информацию о 3 студенте
Введите фамилию и инициалы
Волошко А. А.
Оценки но матем., физике и программир.
4 4 3
***** Введите информацию о 4 студенте
Введите фамилию и инициалы
Долбня В. Е.
Оценки по матем., физике и программир.
5 5 5
***** Введите информацию о 5 студенте
Введите фамилию и инициалы
Ильченко Г. Г.
Оценки по матем., физике и программир.
3 3 4
******* Результаты сессия
Адаменко Р. Л. матем. = 4 физика = 4 програм. = 5 ср. балл = 4.33
Бочаренко П. П. матем. = 5 физика = 5 програм. = 4 ср. балл = 4.67
Волошко А. А. матем. = 4 физика = 4 прогам. = 3 ср. балл = 3.67
Долбня В. Е. матем. = 5 физика = 5 програм. =5 ср. балл = 5
Ильченко Е. Г. матем. = 3 физика = 3 програм. = 4 ср. балл = 3.33
Средний балл группы по математике = 4.2
Средний балл группы по физике = 4.2
Средний балл группы по программированию = 4.2
*******Отличники по программированию:
Адаменко Р.Л.
Долбня В.Е.
Наведемо другий варіант (див. Р8_1_2.СРР) розв’язання поставленої задачі з використанням покажчиків та структуру, яка містить поле оцінок з предметів у вигляді масиву.
//Р8_1_2. СРР — обработка ведомости успеваемости группы студентов //---------------------------------------------- использование указателей #include <iostream.h> #include <string.h> #include <conio.h> void main() { const n = 5, k = 3; /* n — количество студентов,k — количество оценок */ int і, j; float sm, sf, sp; /* sm, sf, sp — сумма оценок группы соответственно по математике, физике, программированию */ struct stud{ char fam[25]; int ocen[3]; // ocen[3] — массив оценок float sb; } ved[n], *pst =& ved[0]; //pst — указатель на массив sm = sf = sp = 0; //---- ввод и обработка информации о студентах и их успеваемости for (і = 0; і < n; i++, pst++) { cout << "Введите информацию о " << (i+i) << " студенте\n"; { cout << "Введите фамилию и инициалы\n"; gets ((* pst).fam); cout << "Оценки по матем., физике и программир. \n"; //-------------------- ввод оценок и подсчет среднего балла студента (*pst).sb=0; for (j = 0; j <3; j++) { сіn >> (*pst).ocen[j]; (*pst).sb += (*pst).ocen[j]; } (*pst).sb=(*pst).sb/3; //средний балл студента за сессию //-------------- суммирование оценок группы студентов по предметам sm += (*pst).ocen[0]; sf+= (*pst).ocen[1]; sp += (*pst).ocen[2]; } } //---------------------------------------- вывод результатов вычислений pst -= n; // pst=&ved[0]; — возвращение указателя на начало массива cout.precision(3); cout <<"****** Результаты сессии"; for (і = 0; і <n; і++, pst++) cout <<і+1<<" "<<(*pst).fam<<" матем. = "<<(*pst).ocen[0]<<" физика = "<<(*pst).ocen[i]<<" програм. = "<<(*pst).ocen[2]<<" ср. балл = "<<(*pst).sb << "\n"; cout << "\n\nСредний балл группы по математике = "<< sm/n; cout << "\nСредний балл группы по физике = "<< sf/n; cout << "\nСредний балл группы по программированию = "<< sp/n; cout << "\n\n*** Отличники по программированию: \n"; pst -= n; for (і = 0; і < n; i++, pst++) if ((*pst).ocen[2] == 5) cout << (*pst).fam <<"\n"; getch (); }
Слід нагадати, що запис виразу з покажчиком, наприклад, (*pst).fam, рівнозначний запису pst -> fam.
В останніх версіях С++ до складу структури можна включати не тільки поля (тобто дані), але і функції обробки полів (методи) (див. розділ 9). Це вдало ілюструє третій варіант (див. Р8_1_3. СРР) розв’язання даного прикладу (середній бал групи з кожного предмета не обчислювався):
// Р8_1_3. СРР — обработка ведомости успеваемости группы студентов //---------------------- структура содержит функции обработки полей #include <iostream.h> #include <string.h> #include <conio.h> void main() { const n=5; int i; //---------------------------------------------------- описание структуры struct Tstud { char fam[15]; int mat, fiz, prg; float sb() //------------ функция подсчета среднего балла { float s = float(mat+fiz+prg)/3; return s;} void wod() //-------------------------- функция ввода данных { cout<<" Введите фамилию\n"; cin>>fam; // фамилии вводятся без инициалов cout<<"Enter ocenki\n"; сіn >> mat >> fiz >> prg;} void vivod() //----------------------- функция вывода данных { cout<<fam<<" mat="<<mat<<" fiz="<<fiz<" prg="<<prg;} } ved[n]; //--------------------------------------------------- ввод исходных данных for (і = 0; і < n; і++) { cout << " Введите информацию о " << (i+1) << " студенте \n"; ved[i].vvod(); } //--------------------------------- вывод результатов вычислений cout << "******* Результаты сессии \n"; for (і = 0; і < n; i++) { ved[i].vivod(); cout.precision(3); cout<<" sb="<<ved[i].sb() << endl; } cout<<"\n*** Отличники по программированию: \n\n"; for (і = 0; і < n; i++) if (ved[i].prg == 5) cout << ved[i].fam << endl; getch (); }
Наведемо приклад, в якому ініціювання масиву типу структура здійснюється під час опису, а в процесі роботи з масивом використовується поняття, що ім’я масиву є покажчиком на масив.
Приклад 8.2. Увести в комп’ютер таку інформацію про монітори: назву, термін гарантії, ціну. Відсортувати монітори за спаданням ціни і вивести відповідні повідомлення.
У програмі в рядках коментарів наведено інший спосіб запису звернення до полів структури.
/* Р8_2. СРР — обработка данных типа структура, работа с именем массива как указателем на массив */ /* сортировка информации о мониторах согласно уменьшению их цены (методом «пузырька») */ #include <iostream.h> #include <conio.h> const int n=6; void main () { struct Monitor { char name[20]; //марка монитора int garant; // срок гарантии float chena; // цена } mon[n]= {" Samsung 757 NF ", 36, 1100.1, " Sony CPD-C520K", 36, 11753.4, " Hansol H95S TFT ", 24, 4260.2, " Samsung 757 DFX ", 12, 1054.5, " LG T710H Flatron 36, 855., " Samsung 192V TFT ", 36, 4252.2 }; // инициализация массива mon[n] при описании int і, k; cout << "\t ******* Spisok monitorov ******* \n"; //--------------------------- вывод информации о мониторах for (і = 0; і < n; і++) { cout<<(*(mon + i)).name; // cout<< (mоn + і) -> name; cout<<(*(mon + i)).garant; // cout<< (mоn + і) -> garant; cout<<(*(mon + i)).chena<< "\n";// cout<< (mоn + і)-> chena<< "\n"; } //------------------------ сортировка no убыванию стоимости Monitor rab; // rab — для перестановки элем. массива for (k = 1; k < n; k++) for (i = 0; і < n-k; i++) if ((*(mon + i)).chena < (*(mon + i+1)).chena) //if ((mon + i) -> chena < (mon + i+1) -> chena) { rab = *(reon + i); * (mon+i) = *(mon+i+1); *(mon + i+1) = rab; } cout << "\n\n\t ******* Sortirovka monitorov ******"\n "; /*-------- вывод информации о мониторах отсортированной по убыванию стоимости */ for (і = 0; і < n; i++) { cout<< " chena: "; cout<< (*(mon +i)).chena; // cout<< (топ + і) -> chena; cout<< " monitor "; cout<< (*(mon + i)).name; // cout<< (топ + і) -> name; cout<< " garantij: "; cout<< (*(mon + i)).garant <<"\n"; // cout<< (mоn + і) -> garant<< "\n"; } getch(); }
Результати обчислень:
******* Spisok monitorov. *******
Samsung 757 NF 36 1100.1
Sony CPD-C520K 36 11753.4
Hansol H95S TFT 24 4260.2
Samsung 757 DFX 12 1054.5
LG T710H Flatron 24 855
Samsung 192V TFT 36 4252.2
******* Sortirovka monitorov *******
chena: 11753.4 monitor Sony CPD-C520K garantij: 36
chena: 4260.2 monitor Hansol H95S TFT garantij: 24
chena: 4252.2 monitor Samsung 192V TFT garantij: 36
chena: 1100.1 monitor Samsung 757 NF garantij: 36
chena: 1054.5 monitor Samsung 757 DFX garantij: 12
chena: 855 monitor LG T710H Flatron garantij: 24
Приклад 8.3. Обробити інформацію про групу студентів, що містить прізвища, стать та середній бал успішності. Вивести повідомлення, чий підсумковий середній бал вищий —юнаків чи дівчат.
Перший варіант програми розв’язання прикладу (див. Р8_3_1.СРР) передбачає, що стать студента при введенні інформації позначається літерами «g» та «b» (від «girl» та«boy»).
// Р8 3 1.СРР — определение среднего балла девушек и юношей /* программа без использования указателей и с использованием имени массива как указателя на массив */ #include<iostream.h> #include<conio.h> const int n=20; void main() { struct { char fam[20]; // фамилия char sex; //пол float sb; // средний балл } stud[n]; // массив студентов float sbg = 0, sbb = 0; // sbg, sbb — средний балл соответственно девушек и юношей int і, kg = 0, kb = 0; //kg, kb — счетчики количества девушек и юношей в группе //-------------------------------------- ввод исходной информации for (і = 0; і < n; i++) { cout << "Input fam"<< endl; // ввод фамилии без инициалов сіn >> stud[i].fam; // сіn >> (*(stud + i)).fam\ сіn >> (stud + i)-> fam; cout <<"Input pol"<< endl; сіn >> stud[i].sex; //сіn>> (*(stud + i)).sex; сіn >> (stud + i) - > sex; cout <<"Input sredniy ball"<< endl; сіn >> stud[i].sb; // сіn >> (*(stud + i)).sb; сіn >> (stud + і) -> sb; } for (i = 0; і < n; i++) if (stud[i].sex == 'g') //if ((*(stud + i))sex == 'g'); if ((stud + i) ->sex == 'g'); /* определение количества девушек в группе и иx суммарного среднего балла */ { kb++; sbg += a[i].sb; //sbg +- ('(stud + i))sb; sbg += (stud + i) -> sb; } else /* определение количества юношей в группе и их суммарного среднего балла */ if (stud[i].sex == 'b') // if (*(stud + і))sex == 'b'); if ((stud + i) -> sex == 'b'); { km++; sbm += stud[i].sb; //sbm += (*(stud + i)).sb; sbm +- (stud + i) -> sb; } cout.precision(2 ); cout << "Sredniy baO girls = " << sbg /kg << endl; cout << "Sredniy ball boys = " << sbm/ kb << endl; if (sbg / kg > sbb / kb) cout << "sredn ball girls bolshe " << endl; else cout << "sredn ball mans bolshe" << endl; getch(); // задержка экрана }
У рядках коментарів програми наведено інший спосіб запису звернення до полів структури для випадку, коли ім’я масиву використане як покажчик на масив.
Другий варіант розв’язання прикладу (див. Р8_3_2.СРР) містить змінну-покажчик та функцію порівняння рядків. При введенні інформації стать студента позначається рядками символів відповідно «girl» та «bоу».
/* Р8_3_2.СРР — определение среднего балла девушек и юношей с использованием указателей и функции сравнения строк strcmp() */ #include <iostream.h> #include <string.h> #include <conio.h> const int n=20; void main() { struct stud { char fam[20]; char sex[4]; float sb; }; stud a[n], *p(&a[0]); float sbg, sbb; int kg, kb, i; //---------------------------------------- ввод информации for (і = 0; і < n; і++, р++) { cout << "Input fam"« endl; сіn >> (*p).fam; // сіn >> p->fam; cout << "Input pol"« endl; сіn >> (*p).sex; // сіn >> p->sex; cout << "Input sredniy ball<< endl; сіn>> (*p).sb; //cin » p->sb; } p-=n; // возвращение указателя в начало массива kg = kb = sbg = sbb = 0; for (і = 0; і < n; i++, p++) //функция strcmp() сравнивает две строки символов if (strcmp((*p).sex, "girl") == 0) //if (strcmp(p->sex, "girt') == 0) { kg++;sbg += (*p).sb; } //sbg += p->sb; else if (strcmp((*p).sex, "boy") == 0) //if (strcmp(p->sex, "man") == 0) { kb++; sbb += (*p).sb; } // sbb += p->sb; cout.precision(2); cout << "Sredniy ball girls = " << sbg /kg << endl; cout << "Sredniy ball boys = " << sbm/ kb << endl; if (sbg/kg > sbb/kb) cout << "sredn ball bolshe "<< endl; else cout << "sredn ball boys bolshe "<< endl; getch (); }