Студопедия.Орг Главная | Случайная страница | Контакты | Мы поможем в написании вашей работы!  
 

Структури мови C



Масив є сукупністю елементів одного типу, а структура є сукупністю елементів різних типів.

Оголошення структури має наступний формат:

struct struct_name

{

type1 member1_name;

type2 member2_name;

typeN memberN_name;

};

Структура оголошується за допомогою ключового слова struct, за яким слідує необов'язкове ім'я нового типу даних (struct_name) та у фігурних дужках шаблон, за яким створюватимуться об'єкти структурного типу. Шаблон містить послідовність оголошень членів структури, кожне з яких завершується крапкою з комою.

Типи оголошуваних членів структури необов'язково мають бути різними, тоді як їх імена повинні попарно відрізнятися.

Наприклад:

struct address

{

char* name; // ім'я "Іваненко Петро Миколайович"

char* street; // вулиця "Зелена"

int house; // номер дому 15

int flat; // номер квартири 2

char* town; // місто "Лисичанськ"

char* region; // область "Луганська"

int zip; // індекс 93107

};

Тут оголошується новий тип користувача з іменем address, який задає шаблон для створення об'єктів поштової адреси, прийнятої в Україні.

Слід звернути увагу в черговий раз на крапку з комою в кінці оголошення: це один з небагатьох в С++ випадків, коли після фігурної дужки потрібна крапка з комою, тому про неї часто забувають.

Змінні типу address можна оголошувати точно так, як і будь-які інші змінні.

Ініціалізувати змінні struct можна так само, як масиви. Наприклад:

address ipm =

{

"Іваненко Петро Миколайович",

"Зелена", 15, 2,

"Лисичанськ",

"Луганська",

};

Об'єкти структурного типу можуть присвоюватися, передаватися як фактичні аргументи функцій і повертатися функціями як результат їх виконання. Наприклад:

address current;

address set_current(address next)

{

address prev = current;

current = next;

return prev;

}

Інші операції, наприклад, порівняння (== та!=), для структурних об'єктів не визначені. Проте користувач може сам визначити ці операції.

Розмір об'єкту структурного типу не обов'язково рівний сумі розмірів всіх його членів. Це відбувається з тієї причини, що архітектури деяких комп'ютерів вимагають розміщувати об'єкти вбудованих типів в апаратно визначених межах (або просто тому, що робота при такому вирівнюванні буде ефективнішою). Типовий приклад - це вирівнювання цілого в| межах слова. В результаті вирівнювання можуть з'явитися "дірки" в структурі.

Структурний тип можна використовувати відразу після його оголошення, ще до того, як буде завершено його повне визначення. Наприклад:

struct link // елемент зв'язного списку

{

link* previous; // попередній елемент

link* successor; // наступний елемент

};

Проте нові об'єкти типу структури не можна оголошувати до тих пір, поки не з'явиться повне визначення структури. Тому оголошення:

struct no_good {

no_good member; // рекурсивне оголошення

};

є помилковим (компілятор не в змозі встановити розмір структури no_good).

Щоб дозволити двом (або більше) структурним типам посилатися один на одного, можна просто оголосити ім'я одного з них як ім'я деякого структурного типу. Наприклад:

struct list; // буде визначено пізніше

struct link

{

link* pre;

link* suc;

list* member_of;

};

struct list

{

link* head;

};

Якби не було першого оголошення list, оголошення члена member_of структури link привело б до синтаксичної помилки.

Можна також використовувати ім'я структурного типу ще до того, як тип буде повністю визначений, якщо тільки це використання не потребує знання розміру структури. Наприклад:

struct S; // S - ім'я деякого типу

extern S а;

S f();

void g(S);

Але приведені оголошення можна використовувати лише після того, як тип S буде визначений:

void h()

{

S а; // помилка: S не визначений, адже для виділення пам'яті

// потрібно знати розмір об'єкту

f(); // помилка: S не визначений, адже для повернення значення

// потрібно знати розмір об'єкту, що повертається

g(a); // помилка: S не визначений, адже для передачі аргументу

// потрібно знати розмір об'єкту, що передається

}

2.5.2.1 Вибір члена структури по імені об'єкту

Якщо відоме ім'я структурного об'єкту, звернення до окремих членів структури здійснюється за допомогою оператора “.” - вибір члена структури або класу по імені об'єкту. Наприклад:

address ipm;

ipm.name = "Іваненко Петро Миколайович";

ipm.house = 15;

2.5.2.2 Вибір члена структури через вказівник на структурний об'єкт

До членів структури часто звертаються через вказівник на структурний об'єкт, використовуючи оператор “->” - вибір члена структури або класу через вказівник. Наприклад:

void print_addr(address* p)

{

cout<< p->name << ‘\n’

<< ”вул. “<< p->street << “, “ << p->house << “, “<< p->flat << '\n'

<< ”м. “ << p->town << '\n'

<< p->region << “ область\n”

<< p->zip << '\n';

}

2.5.2.3 Еквівалентність структурних типів

Два структурні типи вважаються різними навіть тоді, коли вони мають одні й ті ж члени. Наприклад, нижче оголошені різні типи:

struct s1 {int а;};

struct s2 {int а;};

В результаті маємо:

s1 x;

s2 у = x; // помилка: невідповідність типів

Оскільки структурні типи також відрізняються від основних| типів, тому одержимо наступне:

s1 x;

int i = x; // помилка: невідповідність типів

Поля

Для змінної, що приймає тільки два значення (наприклад: так, ні), здається марнотратством використовувати тип char, хоча об'єкт типу char в мові С++ є найменшим об'єктом, який може незалежно розміщуватися в пам'яті.

Проте в мові є можливість зібрати змінні з малими діапазонами значень воєдино, оголосивши їх як поля однієї структури.

Член структури є полем, якщо в його оголошенні після імені вказано число розрядів, яке він повинен займати. Допустимі безіменні поля. Вони не впливають на роботу з поіменованими полями, але можуть поліпшити розміщення поіменованих полів в пам'яті комп'ютера:

struct sreg

{

unsigned enable: 1;

unsigned page: 3;

unsigned: 1; // не використовується

unsigned mode: 2;

unsigned: 4; // не використовується

unsigned access: 1;

unsigned length: 1;

unsigned non_resident: 1;

};

Приведена структура описує розряди нульового регістра стану для старого міні – комп'ютера СМ-4 (передбачається, що поля в слові розміщуються зліва направо). Цей приклад показує також інше можливе застосування полів: давати імена тим частинам об'єкту, розміщення яких в розрядах пам’яті визначено ззовні, наприклад, архітектурою комп'ютера.

Поля повинні мати цілий тип, і вони можуть використовуватися в програмі аналогічно іншим основним об'єктам цілого типу. З цього правила є виключення: не можна брати адресу поля.

В ядрі операційної системи або в програмі - налагоджувачу приведений раніше тип sreg міг би використовуватися таким чином:

sreg *sr0 = (sreg*)0777572;

//...

if (sr0->access) // порушення прав доступу

{

// інструкції аналізу ситуації

sr0->access = 0;

}

Застосовуючи поля для пакування декілька змінних в один байт або слово, ми необов'язково заощадимо пам'ять. Економиться пам'ять для даних, але на більшості машин одночасно зростає кількість команд, необхідних для роботи з запакованими даними.

Крім того, доступ до char або int зазвичай відбувається набагато швидше, ніж доступ до поля. Поля - це просто зручна коротка форма зазначення логічних операцій для добування або занесення інформації в частини слова.

2.5.3 Об'єднання

Об'єднання є сукупністю елементів різних типів, що розміщуються в оперативній пам'яті, починаючи з однієї і тієї ж адреси.

Оголошення об'єднання подібно до оголошення структури, але оголошення об'єднання починається з ключового слова union і має наступний формат:

union union_name

{

type1 member1_name;

type2 member2_name;

typeN memberN_name;

};

Об'єкт типу об'єднання в кожну мить може містити в пам'яті тільки один з вказаних в оголошенні членів. При оголошенні об'єкту типу об'єднання під нього резервується місце для розміщення найбільшого члену об'єднання.

На відміну від структур, об'єкт типу об'єднання може бути ініціалізованим| тільки значенням першого члена об'єднання.

Для розуміння об'єднань спочатку розглянемо структуру - таблицю імен, в якій кожен елемент містить ім'я і значення, що відповідає цьому імені. Значення може задаватися або рядком літер, або цілим числом:

struct entry

{

char* name;

char type;

char* string_value; // використовується, якщо type == 's'

int int_value; // використовується, якщо type == 'i'

};

void print_entry(entry* p)

{

switch(p->type)

{

case 's':

cout << p->string_value;

break;

case 'i':

cout << p->int_value;

break;

default:

cerr << "поле типу помилкове \n";

break;

}

}

Оскільки змінні string_value та int_value ніколи не можуть використовуватися одночасно, очевидно, що частина пам'яті пропадає даремно. Це можна легко виправити, оголосивши обидві змінні як члени об'єднання, наприклад, так:

struct entry {

char* name;

char type;

union

{

char* string_value; // використовується, якщо type == 's'

int int_value; // використовується, якщо type == 'i'

};

};

Тепер гарантується, що при виділенні пам'яті для структурної змінної entry члени string_value та int_value цієї змінної будуть розміщуватися за однією адресою, і при цьому не потрібно міняти всі частини програми, що працюють зі змінною entry.

З описаного вище витікає, що всі члени об'єднання разом займають такий же об'єм пам'яті, який займає найбільший член об'єднання.

Надійний спосіб роботи з об'єднанням полягає в тому, щоб вибирати значення за допомогою того ж самого члена, який його записував. Проте у великих програмах важко гарантувати, що об'єднання використовується тільки у такий спосіб, а в результаті використання не того члена об'єднання можуть виникати помилки, що важко виявляються.

Іноді об'єднання використовують для приведень типів змінних (в основному на це йдуть програмісти, звиклі до мов, в яких немає засобів приведення типів, і в результаті доводиться обдурювати компілятор). Приведемо приклад такого "приведення" int в int*, яке досягається просто іншою інтерпретацією вмісту розрядів:

struct fudge

{

union

{

int i;

int* p;

};

};

fudge а;

a.i = 4095;

int* p = а.p; // не дуже коректне використання

Насправді це зовсім не приведення типів, оскільки можуть знайтися комп'ютери, в яких int і int* можуть займати різні об'єми пам'яті, або комп'ютери, в яких ціле не може розміщуватися за адресою, яка задається непарним цілим числом.

Іноді об'єднання використовують спеціально, щоб уникнути приведення типів. Наприклад, можна використати структурну змінну fudge, щоб дізнатися, як представляється вказівник 0:

fudge.p = 0;

int i = fudge.i; // i необов'язково повинно бути 0

Об'єднанню можна дати ім'я, тобто можна зробити його повноправним типом. Наприклад, fudge можна оголосити так:

union fudge {

int i;

int* p;

};

і використовувати його точно так, як і раніше, коректно або некоректно.





Дата публикования: 2014-11-03; Прочитано: 1041 | Нарушение авторского права страницы | Мы поможем в написании вашей работы!



studopedia.org - Студопедия.Орг - 2014-2024 год. Студопедия не является автором материалов, которые размещены. Но предоставляет возможность бесплатного использования (0.016 с)...