Download as pdf or txt
Download as pdf or txt
You are on page 1of 8

Лекція 12.2.

Структури
Структури. Сортування масивів структур

Структури. Сортування масивів структур


Використання структур дає можливість згрупувати кілька логічно
зв’язаних змінних, типи яких можуть бути різними, надати їм одне ім’я і пра-
цювати з ними, як з цілісним об’єктом. Наприклад, дані про співробітника мо-
жуть об’єднувати таку інформацію: прізвище, ім’я, по батькові, вік, стать.
Структура, як і перераховуваний тип, є користувацьким типом даних — про-
граміст має можливість створювати різні шаблони структур, вказуючи потрібні
при розв’язанні конкретної задачі одиниці інформації (поля). Структури вико-
ристовують в інформаційних системах, базах даних, при роботі зі зв’язними
списками, з деревами тощо. Також для доступу до окремих бітів пам’яті можна
використовувати бітові поля, які є членами структури і розмір яких вказується в
бітах (розглядати не будемо).
Визначення певного типу структури (шаблону структури) і оголошення
змінних цього типу має вигляд:
struct ім’я типу {
поля структури
} список змінних;
Слово struct є зарезервованим словом мови С, яке вказує, що визначається тип
структура. Тут ім’я типу і список змінних не обов’язкові, тобто вони можуть бути
відсутні, але не одночасно (на відміну від перераховуваних даних). Ім’я типу —
ім’я нового типу-структури, який визначається програмістом; поля структури —
описи змінних-елементів, які утворюють структуру (ними можуть бути прості
змінні, вказівники, масиви, рядки символів, інші структури) і мають вигляд тип
ім’я змінної; або тип список імен змінних;; список змінних — імена оголошуваних
змінних-структур чи масивів структур визначеного типу, список змінних може
бути відсутнім, але тоді після закриваючої фігурної дужки обов’язково має бути
крапка з комою (це пов’язано з тим, що визначення типу є оператором).
Наприклад, визначити в програмі тип структури spivrobitnyk з полями priz-
vyshche, imia, pobatkovi, vik, stat можна так:
struct spivrobitnyk {
char prizvyshche[20], imia[20], pobatkovi[20];
int vik;
char stat;
};
У цьому визначенні типу не оголошено жодної змінної-структури. Визна-
чення типу не передбачає виділення пам’яті під структуру, воно просто вказує
її поля, тобто задає шаблон структури.
Після визначення типу spivrobitnyk його можна використати для оголо-
шення, наприклад, змінних-структур dekan, zastupnyk і масиву структур, який
містить дані про 37 викладачів, — vykladach[37]:
struct spivrobitnyk dekan, zastupnyk, vykladach[37];
Для оголошених структур буде виділено пам’ять.
Також вказані структури можна визначити разом з визначенням типу spiv-
robitnyk, об’єднавши попередні два описи:
struct spivrobitnyk {
char prizv[20], imia[20], pobatkovi[20];
int vik;
char stat;
} dekan, zastupnyk, vykladach[37];
Якщо в програмі потрібні тільки певні структури-змінні і в програмі не пе-
редбачається використання інших структур з даними полями, то в описі струк-
тур тип (в даному прикладі spivrobitnyk) вказувати не обов’язково. Можна
тільки визначити поля і вказати структури-змінні:
struct {
char prizv[20], imia[20], pobatkovi[20];
int vik;
char stat;
} dekan, zastupnyk, vykladach[37];
Поля структури можна ініціалізувати при оголошенні:
struct spivrobitnyk dekan = {"Матійчук", "Григорій",
"Петрович",45, 'ч'};
Якщо оголошується масив структур, то кожен елемент масиву структур є
структурою. Доступ до окремої структури масиву структур здійснюється за до-
помогою імені структури й індексу; при цьому, як і в будь-якому масиві, індек-
сація починається з 0.
Доступ до окремих полів (елементів) структури здійснюється за допомо-
гою складних імен:
ім’я змінної-структури.ім’я поля.
Наприклад: dekan.prizv, vykladach[10].prizv, vykladach[10].vik. Поля структури
можна використовувати в будь-якому контексті, де можна використовувати
змінну відповідного типу. Наприклад, можна записати оператори: printf("%s
%c.", vykladach[10].prizv, vykladach[10].imia[0]); — відповідно
буде виведено прізвище одинадцятого за списком викладача і перша буква його
імені; scanf("%s %d", dekan.prizv, &dekan.vik); — тут, оскільки пе-
рший параметр після форматів є рядком, тобто масивом символів, то
dekan.prizv є адресою рядка і & не ставиться; &dekan.vik є адресою цілого числа
vik (при цьому оператор взяття адреси & стоїть перед іменем структури, а не
перед іменем поля); if (vykladach[i].vik>70); тощо.
Полями структури можуть бути масиви (одновимірні чи багатовимірні, зо-
крема масиви символів). Наприклад, щоб одержати доступ до першого елемен-
та поля imia структури vykladach[i], треба вказати таке складне ім’я:
vykladach[i].imia[0] .
Полями структур також можуть бути інші структури. Наприклад, структу-
ра dmr типу data є полем масиву структур student[100] типу dani:
struct data{
int den, misiats, rik;
};
struct dani{
char prizv[20], imia[20];
struct data dmr; // вкладена структура
} student[100];
Тоді, щоб мати доступ до поля rik структури student[j], треба вказати таке
складне ім’я цього поля: student[j].dmr.rik — імена полів вказуються зліва
направо, тобто від зовнішнього до внутрішнього, відповідно до глибини
вкладення.
У мові програмування С структури можна присвоювати, наприкkад: dekan
=zastupnyk; zastupnyk=vykladach[i];. При цьому відбувається побітове
копіювання кожного поля однієї структури у відповідне поле іншої структури.
Але це мають бути тільки структури того самого типу (з тим самим шаблоном);
навіть, якщо структура іншого типу має ідентичні поля, то такі структури не
можна присвоювати повністю, а тільки, використовуючи окремі поля. Також,
якщо поля структур різних типів мають той самий тип даних, то їх можна взає-
мно присвоювати.
Функціям можна передавати як аргументи цілі структури. Якщо структура
є аргументом функції, то вона передається за значенням, тобто створюється її
копія, і будь-які зміни полів структури, яка є формальним параметром ніяк не
відобразяться на фактичному параметрі. При цьому тип фактичного й формаль-
ного параметрів має бути той самий (навіть, якщо структури різних типів мають
ідентичні поля, то вони не можуть бути відповідними параметрами). Крім того,
щоб усі функції програми могли використовувати визначений програмістом тип
структури, він має бути глобальним, тобто визначеним поза функціями, і має
розміщуватися в тексті програми до його використання.
При передачі в функцію структури за значенням у стек викликів структура
копіюється повністю. І якщо структура має велику кількість полів або її полями
є великі масиви, то витрати пам’яті і часу буть суттєвими, стек може швидко
переповнитися. Якщо ж у функцію передавати не структуру, а тільки вказівник
на неї, то у стек передається тільки адреса структури і виклик функції здійсню-
ється набагато швидше. Крім того, при передачі структури за адресою всі її
зміни в функції будуть відображатися на фактичному параметрі.
Вказівники на структури (як і на інші змінні) оголошують за допомогою
символа *, який розміщується перед іменем змінної-структури, наприклад:
struct spivrobitnyk *p_dekan, *p_zastupnyk, *p_vykladach[37];
Для визначення адреси структури використовують операцію взяття адреси
&. Так, щоб одержати адреси структури dekan і окремої структури-елемента
масиву структур vykladach, треба відповідно записати &dekan і &vykladach[i].
Присвоєння вказівникам адрес структур здійснюється так:
p_dekan=&dekan; p_zastupnyk=&zastupnyk;
p_vykladach[i]=&vykladach[i];
Вказівники на структури використовують у двох випадках: для передачі
структур у функції за адресою і для створення динамічних структур даних, на-
приклад, зв’язних списків. Розглянемо тільки передачу структур у функції.
Доступ до окремих полів структури через вказівник на неї можна здійсни-
ти або за допомою складного імені і операції розіменування (*вказівник на
структуру).ім’я поля, наприклад, (*p_dekan).vik, або використавши операцію
стрілка вказівник на структуру->ім’я поля, наприклад p_dekan->vik.
Нехай у програмі визначено глобальний тип структури spivrobitnyk і опи-
сано структуру dekan. У функцію abc як параметр передається адреса цієї стру-
ктури &dekan. У функції змінюється значення поля vik:
...
struct spivrobitnyk {
char prizv[20], imia[20], pobatkovi[20];
int vik;
char stat;
};
void abc(struct spivrobitnyk *p);
int main() {
struct spivrobitnyk dekan={"Матійчук", "Григорій",
"Петрович", 45, 'ч'};
abc(&dekan); // окремої структури масиву: abc(&vykladach[i]);
printf("%s %s %s %d років", dekan.prizv, dekan.imia,
dekan.pobatkovi, dekan.vik);
...
}
void abc(struct spivrobitnyk *p) {
(*p).vik=35;
}
Функції можуть мати тип структури і, отже, повертати структури як ре-
зультат. Наприклад, є функція для введення даних в поля структури і вона по-
вертає структуру:
struct spivrobitnyk vvids() {
struct spivrobitnyk r;
scanf("%s %s %s %d %c", r.prizv, r.imia, &r.vik, &r.stat);
return r;
}
При цьому в функції, яка викликає функцію vvids, має бути, наприклад, такий
оператор dekan=vvids(); .
Приклад 1. Сортування масиву структур. Визначається масив структур,
який містить інформацію про n осіб: прізвище, ім’я і вік. Дані про перших
( n  1 ) особу задаються при визначенні масиву структур шляхом ініціалізації,
про останню особу — вводяться в діалозі. Усі дані про осіб сортуються за віком
за зростанням (застосовано метод бульбашки).
/* Сортування масиву структур за полем rik */
...
#define N 5
struct dani { // дані про особу
char prizv[20]; // прізвище
char imia[20]; // ім'я
int rik; // рік народження
}; // ; обов'язкова в оголошенні структури
struct dani vvids();
void sortbuls(struct dani osoba[], int n);
void vyvids(struct dani osoba[], int n);
/* Робота зі структурами */
int main() {
struct dani osoba[N]={
{"Війтик", "Ольга", 1934},
{"Кублій", "Андрійко", 2021},
{"Ющук", "Іван", 1932},
{"Ющук", "Лариса", 1959}
};
...
osoba[N-1]=vvids(); // функція повертає значення
printf("Початкові дані\n");
vyvids(osoba, N); // передається адреса масиву структур
sortbuls(osoba, N); // передається адреса масиву структур
printf("Відсортовані за роком дані\n");
vyvids(osoba, N); // передається адреса масиву структур
...
}
/* Ввід даних в поля структури */
struct dani vvids() {
struct dani danir;
scanf("%s%s%d", danir.prizv, danir.imia, &danir.rik);
return danir;
}
/* Сортування методом бульбашки за полем "rik" */
void sortbuls(struct dani osoba[], int n) {
struct dani danir;
int i, j;
for (i=0; i<n-1; i++)
for (j=0; j<n-i-1; j++)
if (osoba[j].rik>osoba[j+1].rik) {
danir=osoba[j];
osoba[j]=osoba[j+1];
osoba[j+1]=danir;
}
}
/* Вивід полів структури */
void vyvids(struct dani osoba[], int n) {
int i;
for (i=0; i<n; i++)
printf("\t%s %s %d\n", osoba[i].prizv, osoba[i].imia,
osoba[i].rik);
}
Аналогічний результат можна одержати, якщо функцію vvids, яка повертає
значення-структуру, замінити функцією vvidsa, в яку структура передається за
адресою:
void vvidsa(struct dani *danir);
int main() {
...
vvidsa(&osoba[N-1]);
...
}
/* Ввід даних в поля структури, переданої за адресою */
void vvidsa(struct dani *danir) {
scanf("%s%s%d",(*danir).prizv,
(*danir).imia, &(*danir).rik); // замість (*p).a можна
записати p->a
}
Робота функції vvids, яка повертає значення-структуру, пов’язана з вико-
ристанням додаткової пам’яті і копіюванням даних. Так, у ній для зберігання
введених даних використовується робоча структура, під яку виділяється
пам’ять у стекові викликів. Структура-значення функції (повертається операто-
ром return) запам’ятовується в ділянці пам’яті, на яку вказує вказівник адреси
значення. Також повернута функцією як значення структура присвоюється ло-
кальній структурі функції main. На відміну від функції vvids у функцію vvidsa
передається адреса структури. За рахунок цього дані, які вводяться, зразу запи-
суються в пам’ять фактичного параметра. Таким чином, використання функції
vvidsa, в яку структура передається за адресою, є ефективнішим і щодо часу ви-
конання, і щодо використання пам’яті порівняно з функцією vvids, яка повертає
значення-структуру.

You might also like