
- •1.1 Перші кроки
- •1.2 Змінні й арифметичні вирази
- •1.3 Твердження for
- •1.4 Символічні константи
- •1.5 Ввід і вивід знаків
- •1.5.1 Копіювання файла
- •1.5.2 Відлік символів
- •1.5.3 Відлік рядків
- •1.6 Масиви
- •1.7 Функції
- •1.8 Аргументи - виклик за значенням
- •1.9 Символьні масиви
- •1.10 Зовнішні змінні й область дії
- •2.1 Назви змінних
- •2.2 Типи даних і розміри
- •2.3 Константи
- •2.4 Оголошення
- •2.5 Арифметичні операції
- •2.6 Реляційні та логічні оператори
- •2.7 Перетворення типів
- •2.8 Оператори приросту та спаду
- •2.9 Розрядні оператори
- •2.10 Оператори та вирази присвоєння
- •2.11 Вирази умов
- •2.12 Пріоритет і послідовність обчислення
- •3.1 Вирази та блоки
- •3.3 Else if
- •3.4 Switch
- •3.5 Цикли while та for
- •3.6 Цикли do-while
- •3.7 Break і continue
- •3.8 Goto та мітки
- •4.1 Основні знання про функції
- •4.2 Функції, які не повертають цілих
- •4.3 Зовнішні змінні
- •4.4 Правила області дії
- •4.5 Файли заголовка
- •4.6 Статичні змінні
- •4.7 Регістрові змінні
- •4.8 Структура блоків
- •4.10 Рекурсія
- •4.11 Препроцесор c
- •4.11.1 Включення файлів
- •4.11.2 Заміна макросів
- •4.11.3 Обумовлене включення файлів
- •5.1 Покажчики й адреси
- •5.2 Покажчики й аргументи функцій
- •5.3 Покажчики та масиви
- •5.4 Арифметика адрес
- •5.5 Покажчики на символи та функції
- •5.6 Масив покажчиків; покажчики на покажчики
- •5.7 Багатовимірні масиви
- •5.8 Ініціалізація масиву покажчиків
- •5.9 Покажчики в порівнянні з багатовимірними масивами
- •5.10 Аргументи командного рядка
- •5.11 Покажчики на функції
- •5.12 Складні оголошення
- •6.1 Основні поняття про структури
- •6.2 Структури та функції
- •6.3 Масиви структур
- •6.4 Покажчики на структури
- •6.5 Структури зі зворотнім звертанням
- •6.6 Пошук по таблиці
- •6.7 Typedef
- •6.8 Сполуки
- •6.9 Розрядні поля
- •7.1 Стандартний ввід і вивід
- •7.2 Форматований вивід - printf
- •7.3 Списки аргументів довільної довжини
- •7.4 Форматований ввід - scanf
- •7.5 Доступ до файлів
- •7.6 Обробка помилок - stderr і exit
- •7.7 Ввід і вивід рядків
- •7.8 Додаткові функції
- •7.8.1 Операції з ланцюжками
- •7.8.2 Перевірка і перетворення класів символів
- •7.8.3 Ungetc
- •7.8.4 Виконання команд
- •7.8.5 Керування пам'яттю
- •7.8.6 Математичні функції
- •7.8.7 Генератор випадкових чисел
- •8.1 Дескриптори файлів
- •8.2 Низькорівневий ввід/вивід - read і write
- •8.3 Open, creat, close, unlink
- •8.4 Довільний доступ - lseek
- •8.5 Приклад: втілення fopen і getc
- •8.6 Приклад - перелік вмісту каталогів
- •8.7 Приклад - розподільник пам'яті
6.2 Структури та функції
Єдиними чинними операціями зі структурами являються їхнє копіювання, або присвоєння значення їм як цілому, здобуття їхньої адреси за допомогою &, та доступ до її членів. Копіювання та присвоєння включають передачу аргументів функціям так само як повернення значень функціями. Структури неможливо порівняти. Структуру можна ініціювати списком сталих значень членів структури; можна також започаткувати автоматичну структуру за допомогою присвоєння.
Давайте займемося дослідженням структур шляхом написання декількох функцій для роботи з пунктами прямокутника. Існує, принаймні, три можливих підходи до цієї проблеми: передача складових окремо, передача цілої структури, або передача покажчика на неї. Кожна з цих метод має свої переваги й недоліки.
Перша функція makepoint візьме в якості аргументів два цілих, і поверне структуру point:
/* makepoint: утворює пункт зі складників x та y */
struct point makepoint(int x, int y)
{
struct point temp;
temp.x = x;
temp.y = y;
return temp;
}
Зверніть увагу, що між назвою аргументу й елементом структури з тим самим ім'ям конфлікту не має; навпаки, повторне використання назви тільки підкреслює взаємозалежність.
makepoint тепер можна використати для динамічної ініціалізації структури, або подання структури як аргумент функції:
struct rect screen;
struct point middle;
struct point makepoint(int, int);
screen.pt1 = makepoint(0,0);
screen.pt2 = makepoint(XMAX, YMAX);
middle = makepoint((screen.pt1.x + screen.pt2.x)/2,
(screen.pt1.y + screen.pt2.y)/2);
Наступним кроком є створення набору функцій для арифметичних дій з пунктами. Наприклад
/* addpoints: додає два пункти */
struct addpoint(struct point p1, struct point p2)
{
p1.x += p2.x;
p1.y += p2.y;
return p1;
}
У цьому випадку, як аргументи, так і повернене значення функції являються структурами. Ми збільшили складові p1 замість використання тимчасової змінної, щоб підкреслити, що структури, в якості параметрів, передаються за значенням, як і інші параметри функцій.
Як інший приклад, функція ptinrect перевіряє, чи пункт знаходиться всередині прямокутника, основуючись на нашій умові, що прямокутник включає ліву та нижню межу, а не верхню та праву:
/* ptinrect: повертає 1, якщо p всередині r, 0 - якщо ні */
int ptinrect(struct point p, struct rect r)
{
return p.x >= r.pt1.x && p.x < r.pt2.x
&& p.y >= r.pt1.y && p.y < r.pt2.y;
}
Це передбачає, що прямокутник представлено в стандартній формі, де координати pt1 менші за координати pt2. Наступна функція повертає прямокутник, гарантовано в канонічній формі представлення:
#define min(a, b) ((a) < (b) ? (a) : (b))
#define max(a, b) ((a) > (b) ? (a) : (b))
/* canonrect: стандартизує координати прямокутника */
struct rect canonrect(struct rect r)
{
struct rect temp;
temp.pt1.x = min(r.pt1.x, r.pt2.x);
temp.pt1.y = min(r.pt1.y, r.pt2.y);
temp.pt2.x = max(r.pt1.x, r.pt2.x);
temp.pt2.y = max(r.pt1.y, r.pt2.y);
return temp;
}
Якщо функції потрібно передати велику структуру, то загалом ефективніше вказати покажчик, чим копіювати цілу структуру. Покажчики на структуру подібні на покажчики на звичайні змінні. Оголошення
struct point *pp;
вказує на те, що pp являється покажчиком на структуру типу struct point. Якщо pp вказує на структуру point, то *pp — це сама структура, a (*pp).x та (*pp).y — це члени структури. Для використання pp, ми могли би написати, наприклад,
struct point origin, *pp;
pp = &origin;
printf("origin is (%d,%d)\n", (*pp).x, (*pp).y);
Дужки обов'язкові у випадку (*pp).x, оскільки пріоритет оператора елемента структури .більший за *. Вираз *pp.x означає *(pp.x), що неправильно тому, що x не являється покажчиком.
Покажчики на структури використовуються настільки часто, що було надано альтернативне позначення для скорочення. Якщо p — це покажчик на структуру, тоді
p->член-структури
посилається на певний елемент. Таким чином, ми могли би написати натомість
printf("origin is (%d,%d)\n", pp->x, pp->y);
Обидва, . та -> спрягаються з ліва на право, тож якщо ми матимемо
struct rect r, *rp = &r;
то наступні чотири вирази еквівалентні:
r.pt1.x
rp->pt1.x
(r.pt1).x
(rp->pt1).x
Структурні оператори . із ->, разом із () виклику функцій і [] індексів знаходяться на верхівці ієрархії пріоритету, тож спрягаються дуже тісно. Отже, наприклад, якщо ми маємо оголошення
struct {
int len;
char *str;
} *p;
то
++p->len
здійснює приріст len, а не p, оскільки неявні дужки виглядають як ++(p->len). Дужки можна використати, щоб змінити спрягання (зв'язування): (++p)->len збільшує p перед тим як дістатися до len, тоді як (p++)->len збільшує p після. (Цей останній набір дужок необов'язковий.) Подібно до цього, *p->str добуває значення, на яке вказує str; *p->str++здійснює приріст str після доступу до того, на що вона вказує (схоже до *s++); (*p->str)++збільшує те, на що вказує str; a *p++->str збільшує p після доступу до того, на що вказуєstr.