Єдиними чинними операціями зі структурами являються їхнє копіювання, або присвоєння значення їм як цілому, здобуття їхньої адреси за допомогою &, та доступ до її членів. Копіювання та присвоєння включають передачу аргументів функціям так само як повернення значень функціями. Структури неможливо порівняти. Структуру можна ініціювати списком сталих значень членів структури; можна також започаткувати автоматичну структуру за допомогою присвоєння.
Давайте займемося дослідженням структур шляхом написання декількох функцій для роботи з пунктами прямокутника. Існує, принаймні, три можливих підходи до цієї проблеми: передача складових окремо, передача цілої структури, або передача покажчика на неї. Кожна з цих метод має свої переваги й недоліки.
Перша функція 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.