Структури в мові C¶
Що таке структура?¶
Структура (struct) — це спосіб об'єднати кілька змінних різних типів в одну сутність.
Аналогія
Уяви анкету студента: ім'я, вік, середній бал. Це три різні типи даних, але вони належать одній людині. Структура дозволяє зберігати їх разом.
Навіщо потрібні структури?¶
Можна обійтися без структур, наприклад ми вже вміємо групувати дані у масиви, і тут ми могли б використати три окремі масиви:
// Bad: data is scattered across different entities
char names[100][50];
int ages[100];
float grades[100];
// Why bad: For example, to get student #5 data:
std::cout <<
names[5] << ", " <<
ages[5] << " years, grade: " <<
grades[5] << std::endl;
Структури дозволяють нам логічно згрупувати дані ніби в одну змінну. Нижче приклад використання структури, звернення до пам'яті та читання даних
// Example of single student:
Student john;
//...
std::cout <<
john.name << ", " <<
john.age << " years, grade: " <<
john.grade << std::endl;
// Even array of students:
Student students[100];
//...
// To print all data about students:
for (int i = 0; i < 100; i++)
{
std::cout <<
students[i].name << ", " <<
students[i].age << " years, grade: " <<
students[i].grade << std::endl;
}
students[i] поверне одного студента. Для звернення до його полів даних
відбувається через оператор ., це спеціальний оператор в С, який повертає
поле даних структури (як звичайну змінну відповідного типу)
Оголошення структури¶
Оголошуючи структуру, ми повідомляємо компілятору ніби новий тип даних. Для прикладу, що мав місце вище оголошення має бути приблизно таким:
Після цього, коли компілятор побачить визначення нової змінної:
він (компілятор) виділить пам'яті достатньо для збереження всіх полів структури, відповідно до того ми оголосили структуру ранішеflowchart LR
subgraph Student["struct Student"]
direction LR
A["char name[50]"]
B["int age"]
C["float grade"]
end
Не забудь крапку з комою!
Після закриваючої фігурної дужки структури обов'язково ставиться ;
Створення змінної-структури¶
Спосіб 1: Окремо від оголошення¶
struct Student {
char name[50];
int age;
float grade;
};
int main() {
struct Student student1; // Created a variable of type struct Student
return 0;
}
Спосіб 2: Відразу при оголошенні¶
struct Student {
char name[50];
int age;
float grade;
} student1, student2; // Created two variables immediately
Спосіб 3: З typedef (рекомендовано!)¶
typedef struct {
char name[50];
int age;
float grade;
} Student; // Now you can write just Student instead of struct Student
int main() {
Student student1; // Shorter and more convenient!
return 0;
}
Рекомендація
Завжди використовуй typedef — це робить код чистішим і схожим на інші мови програмування.
Доступ до полів структури¶
Використовуй оператор крапка . для доступу до полів:
#include <iostream>
#include <cstring>
typedef struct {
char name[50];
int age;
float grade;
} Student;
int main() {
Student s;
// Writing data
strcpy(s.name, "Ivan"); // For strings we use strcpy
s.age = 16;
s.grade = 10.5;
// Reading data
std::cout << "Student: " << s.name << std::endl;
std::cout << "Age: " << s.age << std::endl;
std::cout << "Grade: " << s.grade << std::endl;
return 0;
}
Вивід:
Ініціалізація структури¶
При оголошенні¶
Student s1 = {"Maria", 17, 11.2};
// Or with named fields (C99+):
Student s2 = {
.name = "Petro",
.age = 16,
.grade = 9.8
};
Копіювання структур¶
Структури можна копіювати простим присвоєнням:
Student s1 = {"Ivan", 16, 10.5};
Student s2 = s1; // Copied all fields!
std::cout << s2.name << std::endl; // Output: Ivan
Масив структур¶
#include <iostream>
typedef struct {
char name[50];
int age;
float grade;
} Student;
int main() {
Student class_10a[3] = {
{"Ivan", 16, 10.5},
{"Maria", 15, 11.8},
{"Petro", 16, 9.2}
};
// Print all students
for (int i = 0; i < 3; i++) {
std::cout <<
class_10a[i].name << ": " <<
class_10a[i].grade << std::endl;
}
return 0;
}
Вивід:
Вказівники на структури¶
Оператор стрілка ->¶
Коли маємо вказівник на структуру, використовуємо -> замість .:
#include <iostream>
typedef struct {
char name[50];
int age;
} Student;
int main() {
Student s = {"Ivan", 16};
Student *ptr = &s; // Pointer to structure
// Two equivalent ways:
std::cout << (*ptr).name << std::endl; // Method 1: dereference + dot
std::cout << ptr->name << std::endl; // Method 2: arrow (more convenient!)
return 0;
}
flowchart LR
subgraph Stack["Memory"]
ptr["ptr<br/>(pointer)"] --> s
subgraph s["s (Student)"]
name["name: Ivan"]
age["age: 16"]
end
end
Правило
- Змінна структури → використовуй
. - Вказівник на структуру → використовуй
->
Структури як параметри функцій¶
Передача за значенням (копіюється)¶
void printStudent(Student s) {
std::cout << s.name << ", " << s.age << " years" << std::endl;
}
int main() {
Student ivan = {"Ivan", 16};
printStudent(ivan); // A COPY is passed
return 0;
}
Увага
При передачі за значенням копіюється вся структура. Для великих структур це повільно!
Передача за вказівником (ефективно)¶
void printStudent(Student *s) {
std::cout << s->name << ", " << s->age << " years" << std::endl;
}
void birthday(Student *s) {
s->age++; // Modifying the original!
}
int main() {
Student ivan = {"Ivan", 16};
printStudent(&ivan); // Passing address
birthday(&ivan); // Incrementing age
printStudent(&ivan); // Now 17 years
return 0;
}
Вивід:
Вкладені структури¶
Структура може містити іншу структуру:
typedef struct {
int day;
int month;
int year;
} Date;
typedef struct {
char name[50];
Date birthday; // Nested structure
} Student;
int main() {
Student s = {
"Ivan",
{15, 3, 2008} // day, month, year
};
std::cout <<
s.name << " was born on " <<
s.birthday.day << "." <<
s.birthday.month << "." <<
s.birthday.year << std::endl;
return 0;
}
flowchart LR
subgraph Student
direction LR
A["name: Ivan"]
subgraph Date["birthday (Date)"]
direction LR
B["day: 15"]
C["month: 3"]
D["year: 2008"]
end
end
Розмір структури в пам'яті¶
Розмір структури можна дізнатися через sizeof:
typedef struct {
char c; // 1 byte
int i; // 4 bytes
double d; // 8 bytes
} Example;
int main() {
std::cout << "Size of char: " << sizeof(char) << std::endl;
std::cout << "Size of int: " << sizeof(int) << std::endl;
std::cout << "Size of double: " << sizeof(double) << std::endl;
std::cout << "Size of struct: " << sizeof(Example) << std::endl;
return 0;
}
Цікаво
1 + 4 + 8 = 13 байтів, але sizeof(Example) може показати 16 або 24 байти!
Це через вирівнювання пам'яті (padding) — компілятор додає порожні байти для оптимізації.
Практичний приклад: Точка та відстань¶
#include <iostream>
#include <cmath>
typedef struct {
double x;
double y;
} Point;
double distance(Point a, Point b) {
double dx = b.x - a.x;
double dy = b.y - a.y;
return sqrt(dx * dx + dy * dy);
}
int main() {
Point p1 = {0.0, 0.0};
Point p2 = {3.0, 4.0};
std::cout << "Distance: " << distance(p1, p2) << std::endl; // 5.00
return 0;
}
Формула відстані між точками:
Задачі для практики¶
Задача 1: Прямокутник
Створи структуру Rectangle з полями width і height.
Напиши функції area() і perimeter().
Задача 2: Комплексні числа
Створи структуру Complex для комплексних чисел (реальна та уявна частини).
Напиши функцію додавання двох комплексних чисел.
Задача 3: Список студентів
Створи масив з 5 студентів, заповни їх даними і знайди студента з найвищим балом.
Підказка
Використай цикл for і змінну для зберігання індексу найкращого студента.
Далі: Алгоритми