Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Мова програмування С.docx
Скачиваний:
3
Добавлен:
01.05.2025
Размер:
490 Кб
Скачать
☆

4.11.3 Обумовлене включення файлів

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

Рядок #if обчислює константний цілочисельний вираз (який не може включати sizeof, зведення типів або констант енумерації). Якщо вираз не є нульовим, всі наступні рядки до#endif або #elif або ж #else буде включено. (Препроцесорний вираз #elif подібний доesle-if.) Вираз defined(назва) після #if дорівнюватиме 1, якщо назву було означено і 0, якщо ні.

Наприклад, щоб упевнитись, що вміст файла hdr.h включено тільки один раз, сам вміст оточується умовою на зразок наступної:

#if !defined(HDR)

#define HDR

/* тут йде вміст hdr.h */

#endif

Перше включення hdr.h визначає ім'я HDR; наступні включення знаходитимуть це ім'я означеним і стрибатимуть далі аж до #endif. Цей стиль можна використати і щоб запобігти багаторазового включення тих самих файлів. Якщо послідовно впроваджувати даний стиль, тоді кожний файл заголовка може самостійно включати будь-які інші файли заголовка, від яких він залежить, звільняючи користувача заголовка від клопоту про взаємозалежності.

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

#if SYSTEM == SYSV

#define HDR "sysv.h"

#elif SYSTEM == BSD

#define HDR "bsd.h"

#elif SYSTEM == MSDOS

#define HDR "msdos.h"

#else

#define HDR "default.h"

#endif

#include HDR

Вирази #ifdef і #ifndef являються спеціалізованою формою, яка перевіряє чи ім'я означено, чи ні. Перший приклад з #if вище можна було також написати як

#ifndef HDR

#define HDR

/* тут йде вміст hdr.h */

#endif

Розділ 5: Покажчики та масиви

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

Покажчики іноді поєднують з твердженнями goto, як чудовий спосіб створення програм, які неможливо зрозуміти. Це правда, якщо користуватися ними недбало. Так само досить легко створити покажчики які вказують кудись непередбачувано. Однак, за певної дисципліни, завдяки покажчикам можна досягти ясності та простоти. Саме цю їхню рису ми намагатимемось проілюструвати.

Основною зміною ANSI C було внесення ясності щодо того, як можна маніпулювати покажчиками, фактично роблячи обов'язковим те, що хороші програмісти практикують, і до чого хороші компілятори примушують. На додаток, тип void * (порожній покажчик) замінює char *, як належний тип загального покажчика.

5.1 Покажчики й адреси

Почнімо зі спрощеного зображення того як організовано пам'ять. Типова машина має масив послідовно нумерованих або адресованих комірок (секцій) пам'яті, якими можна орудувати окремо або прилеглими групами. Поширеним випадком є, коли один байт може бути char, тоді як пара однобайтових комірок розглядається як коротке ціле (short int), а чотири суміжних байти утворюють довге ціле. Покажчик — це група комірок (часто дві або чотири), що можуть утримати адресу. Тож, якщо c — це char, а p — це покажчик, що вказує на адресу c, то ми можемо графічно зобразити цю ситуацію як наступне:

.---------------------.

| |

p: | c: V

------+---+---+---+---------+---+---+---+---+------

|:::|:::|:::| |:::|:::|:::|:::|

|:::|:::|:::| |:::|:::|:::|:::|

------+---+---+---+---------+---+---+---+---+------

Унарний оператор & добуває адресу об'єкта, тож твердження

p = &c;

присвоює адресу c змінній p, і p, як говорять, «вказує на» c. Оператор & застосовується лише з об'єктами з пам'яті — змінними й елементами масивів. Його неможливо використати з виразами, константами або регістровими змінними.

Унарний * є оператором непрямого звертання або «розіменування». Коли його застосовано до покажчика, він дає доступ до об'єкта, на який вказує покажчик. Припустімо, що x та y є цілими, а ip — це покажчик на int. Наступний штучний приклад демонструє як оголосити покажчик і як користуватися & та *:

int x = 1, y = 2, z[10];

int *ip; /* ip є покажчиком на int */

ip = &x; /* ip тепер вказує на x */

y = *ip; /* y дорівнює тепер 1 */

*ip = 0; /* x дорівнює тепер 0 */

ip = &z[0]; /* ip тепер вказує на z[0] */

Оголошення змінних x, y та z нам зрозуміле. Оголошення ж покажчика ip

int *ip;

задумане як мнемоніка (як символічне); воно вказує на те, що вираз *ip є типу int. Синтаксис оголошення змінної імітує синтаксис виразів, в яких змінна може з'явитися. Ця сама система застосовується також при оголошенні функцій. Наприклад,

double *dp, atof(char *);

вказує на те, що вирази *dp і atof(s) повернуть значення типу double і, що аргументом atofявляється покажчик на char.

Ви також повинні звернути увагу на те, що покажчик обмежений вказувати тільки на окремий рід об'єктів: кожний покажчик вказує на певний тип даних. (Існує один виняток — «покажчик наvoid» (порожній покажчик), використовуваний для утримування будь-якого типу покажчиків, але до якого неможливо непрямо звернутися. Ми повернемося до нього у Розділі 5.11.)

Якщо ip вказує на ціле x, тоді *ip може з'являтися в будь-якому контексті, в якому може x, тож

*ip = *ip + 10;

збільшує *ip на 10.

Унарні оператори * та & прив'язані тісніше ніж арифметичні оператори, тож присвоєння

y = *ip + 1;

візьме те, на що вказує ip, додасть 1 і присвоїть отриманий результат y, тоді як

*ip += 1;

здійснює приріст того, на що вказує ip; те саме стосується

++*ip;

та

(*ip)++;

В останньому прикладі, дужки обов'язкові; без них, вираз збільшив би саму ip (тобто адресу) амість того, на що вона вказує, оскільки унарні оператори такі як * й ++ асоціюються (спрягаються) зправа наліво.

І, нарешті, оскільки покажчики, це також змінні, їх можна виживати без непрямого звертання. Наприклад, якщо iq — це інший покажчик на int, то

iq = ip;

копіює вміст ip до iq, таким чином примушуючи iq вказувати на той самий об'єкт, на який вказує ip.