Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Зубенко, Омельчук - Програмування. Поглиблений курс

.pdf
Скачиваний:
49
Добавлен:
07.03.2016
Размер:
4.72 Mб
Скачать

 

 

 

 

 

Розділ ІІІ. МОВИ ПРОГРАМУВАННЯ С ТА С++

 

 

 

Таблиця 3.1. Ключові слова мови C (С99)

 

 

 

 

 

auto

 

double

 

int

 

struct

 

break

 

else

 

 

 

 

 

 

 

 

 

 

register

 

typedef

 

char

 

extern

 

return

 

void

 

 

 

unsigned

 

default

 

for

 

signed

 

union

 

do

 

 

 

volatile

 

continue

 

enum

 

short

 

while

 

static

 

 

 

_Bool

 

_Imaginary

 

restrict

 

_Complex

 

inline

 

long

 

 

 

const

 

switch

 

float

 

sizeof

 

goto

 

case

 

 

 

if

 

 

 

 

 

 

 

 

 

 

 

 

Константи. У C виділяють чотири типи констант: цілі, константи з рухомою точкою, символьні й літерали.

Увага! Розпочинаючи з визначення символьних констант і далі, для опису синтаксичних конструкцій будемо користуватися розширеними БНФ (див. підрозд. 1.4.4). Для перенесення продовження конструкції на наступний рядок будемо використовувати символ бек-слеш \

Символьні константи формуються з літер алфавіту й записуються за допомогою апострофа:

<символьна-константа>::=<вхідний-символ-кодової-таблиці>' | <керівна-послідовність>' | L<широка-символьна-константа>'

Керівні послідовності наведено в табл. 3.2. Вони подають деякі ке- рівні символи, а також дозволяють задавати всі літери кодової табли- ці за допомогою їхніх числових кодів (вісімкових і шістнадцяткових). При цьому довжина вісімкової послідовності обмежена трьома літе- рами, а шістнадцяткової довільна. С99 забороняє всі коди, що ви- ходять за межі беззнакового символьного типу (unsigned char).

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

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

Таблиця 3.2. Керівні послідовності

Керівна по-

Значення

Десяткова

Шістнадцят-

Символьна

слідовність

 

 

кова

 

\a

Дзвоник

7

0x07

BEL

\b

Крок_назад

8

0x08

BS

\t

Горизонталь-

9

0x09

HT

 

на_табуляція

 

 

 

271

ПРОГРАМУВАННЯ

Закінчення табл. 3.2.

Керівна по-

Значення

Десяткова

Шістнадцят-

Символьна

слідовність

 

 

кова

 

\n

Новий_рядок

10

0x0a

LF

\v

Вертикаль-

11

0x0b

VT

\r

на_табуляція

 

 

CR

Повернен-

13

0x0d

\f

ня_каретки

 

 

FF

Нова_сторінка

12

0x0c

\"

Подвійна_лапка

34

0x22

"

\'

Апостроф

39

0x27

\0

Нуль-символ

0

0x00

 

\\

Бек-слеш

92

0x5c

\

\?

Знак_питання

63

0x3f

?

\ццц8

Символ із вісімко-

ццц(8)

( ццц(8) ) (16)

Ідеограма

 

вим кодом ццц(8)

 

 

символу

\xц…ц16

Символ із шістнадця-

цц(16)

цц16

Ідеограма

 

тковим кодом цц(16)

 

 

символу

Приклад 3.2. Символьні константи:

1)символи в апострофах: ‘a', ‘А' , ‘/', ‘9';

2)вісімкові й шістнадцяткові коди: ‘\007', ‘\x7' символ Дзвоник;

3)у вхідному рядку \0007 препроцесор розпізнає два символи: нуль-символ і ‘7', а в рядку abc\025\xFF 125 дев'ять cимволів ‘a',

‘b', ‘c', ‘\025', ‘\xFF',‘ ‘,'1', ‘2', ‘5'

Літерали це послідовність літер, розміщених у подвійних лапках: "…" (не плутати символ подвійних лапок із двома апострофами). Підкре- слюємо, що до складу літерала входять саме літери, а не символьні конс- танти в апострофах. Наприклад, "факультет кібернетики". Лапки не входять до складу літерала. Для включення в літерал подвійних лапок і літер \ та LF використовуються керівні послідовності відповідно \",\\ та \n. Наприклад, "\"Гайдамаки\"\n" правильний літерал.

Зберігаються літерали у статичній пам'яті. Якщо n довжина літе- рала, то для нього буде виділено n +1 байтів пам'яті по одному для коду кожної літери й додаткового символу ‘\0' із кодом 0. Останній додається обов'язково як ознака закінчення поля літерала.

Приклад 3.3. Літерали в пам'яті

Літерал

 

 

 

 

Подання в пам'яті

 

 

 

 

""

0

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

"UEFA-

85

69

70

65

 

45

50

48

 

49

50

10

0

2012\n"

 

 

 

 

 

 

 

 

 

 

 

 

 

"25/*int*/"

50

53

47

42

 

105

110

116

 

42

47

0

 

"\"Дніпро\""

34

E4

ED

B3

 

EF

F0

EE

 

34

0

 

 

272

Розділ ІІІ. МОВИ ПРОГРАМУВАННЯ С ТА С++

Упершому рядку подано порожній літерал. Довжина його нульова,

адовжина поля в пам'яті становить 1. У другому рядку літерал міс- тить керівний символ LF новий_рядок із кодом 10, у четвертому

усередині лапки " Операції й роздільники. Лексеми операцій і пунктуатори наведе-

ні в табл. 3.3.

Таблиця 3.3. Лексеми операцій і пунктуатори

Підклас лексем

Лексеми

Прості операції

!|%|^|&|*|-|+|=|~|||.|,|<|>|/|?|,

Cкладені присвоювання

+=|-=|*=|/=|%=|<<=|>>=|&=|^=||=

Cкладені операції

->|++|--|<<|>>|<=|>=|==|!=|&&

Пунктуатори

(|)|[|]|{|}|;|:|#|'|"|\

Коментар це довільна послідовність символів, розташована між парами символів /* та */, що не містить усередині іншої пари */. У стандарті С99 коментарями є також підрядки, що розпочинаються двома символами слеш // і закінчуються символом LF. Коментарі не розпізнаються як лексеми в символьних константах і літералах (див. прикл. 3.3), не задають жодних дій і замінюються препроцесором на один пробіл. Їхнє основне призначення пояснити в тексті програми окремі її конструкції (див. прикл. 3.5).

Увага! У програмах будь-які сусідні константи, ідентифікатори та службові слова мають розділятися одним або кількома порожніми си- мволами. Пробіли між сусідніми лексемами необов'язкові, якщо од- нією з них є роздільник або символ операції ►

3.1.3. СТРУКТУРА ПРОГРАМ

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

C-програма – це послідовність функцій, директив препроцесора й операторів опису й декларації даних, яка обов'язково містить функцію з іменем main.

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

273

ПРОГРАМУВАННЯ

сування. Це, окрім інших переваг, сприяє скороченню текстів про- грам. Заголовні файли можуть самі використовувати інші заголовні файли. Подібні підключення здійснює директива #іnclude:

<директива_підключення>::= #іnclude \<file1\>| #іnclude "file2" 23

Файли в обох варіантах директиви відрізняються лише місцем їх- нього розташування. Перший файл file1 препроцесор буде шукати тільки у стандартних бібліотеках, наприклад у папці INCLUDE, а файл file2 там само, але після того, як його не буде знайдено в поточно- му каталозі. Зазвичай це файли, підготовлені самим програмістом. Дія директиви полягає у вилученні самої директиви з тексту програ- ми і вставленні (підключенні) на її місце всього вмісту текстових фай-

лів file1 або file2.

Препроцесорні константи. Препроцесор дозволяє вводити в

текст програм власні (препроцесорні ) константи:

#define <ІМ'Я-КОНСТАНТИ> <слово>

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

Приклад 3.4. Наведені директиви підключають до програми заголов- ний файл <stdio.h>, що підтримує стандартні функції консольного вве- дення-виведення, і визначають препроцесорні константи N та ALPHA:

# include <stdio.h> /*необхідний для забезпечення в/в*/ #define N 10000

#define N=10000 /*невірна директива: між N та 10000 відсутній пробіл*/

#define ALPHA "abcdefgheijklmnopqrstuvwxyz"

Роздільна компіляція С-програми. Складові елементи C-програми розміщуються в текстових файлах, що належать певній одиниці компі- ляції. Окремий файл може містити кілька елементів із набору, а вся про- грама складатися з кількох одиниць компіляції (роздільна компіляція).

23 У формулі праворуч літери ‘<’ ,‘>’ є термінальними, а ліворуч метасимволами.

274

Розділ ІІІ. МОВИ ПРОГРАМУВАННЯ С ТА С++

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

Файли C-програм бувають двох типів: заголовні, або h-файли (ма- ють розширення .h), і C-файли (мають розширення .c). Імена С++- файлів мають розширення .cpp, .cxx або .cc.

Щоб отримати уявлення про C-програму, розглянемо приклад про- стої програми з кількома одиницями компіляції, а потім повернемося до загальної структури програм.

Приклад 3.5. Роздільна компіляція. Нехай необхідно створити про- граму prog, що знаходить куб суми й суму кубів двох введених із кла- віатури чисел і виводить їх на екран.

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

Зауважимо, що у С-програмах діє правило: першій появі виклику будь-якої функції (у тому числі й стандартної) має передувати її про- тотип заголовок із символом ‘;' у кінці. Прототипи стандартних фун- кцій розташовані в заголовних файлах, прототипи решти безпосе- редньо в текстах С-програм. Щоб прототип потрібної стандартної фу- нкції потрапив у текст С-програми, необхідно до неї підключити від- повідний заголовний файл. Таке підключення здійснюється директи- вою препроцесора #include і зводиться до текстової заміни директи- ви на текст заголовного файла, що підключається.

Лістинг funcs.c:

#include <math.h> /*підключення заголовного файла math.h, що містить прототип стандартної функції pow для обчислення степеня*/24

/*опис функції обчислення куба суми x та y*/ int cube_sum(int x, int y)

{

return pow(x+y,3);

}

/*опис функції обчислення суми кубів x та y*/ int sum_cube(int x, int y)

{

return pow(x,3)+pow(y,3);

}

24 pow(x,y)= xy .

275

ПРОГРАМУВАННЯ

Файл start.c містить основну частину програми, в якій вводяться з клавіатури два дійсні числа й за допомогою функцій cube_sum і sum_cube знаходяться потрібні числа, які, у свою чергу, виводяться на екран. Для того, щоб програмно об'єднати файли funcs.c та start.c, в останній вводять прототипи функцій cube_sum та sum_cube з інформацією про те, що їхні описи містяться в іншому файлі (кваліфікатор extern).

Лістинг start.c:

#include <stdio.h> /*підключення файла stdio.h із прототипами стандартних функцій введення scanf і виведення printf*/

/*прототипи зовнішніх функцій cube_sum та sum_cube*/ extern int cube_sum(int x, int y);

extern int sum_cube(int x, int y);

/*maim – головна функція*/ int main()

{int x,y;

/*читання з клавіатури значень і присвоювання їх змінним x,y*/ scanf("%d%d", &x,&y); /*про функцію scanf див. підрозд.

3.8.2*/

/*виведення

на екран значень

функцій

cube_sum(x,y) та

sum_cube(x,y)*/

cube_sum=%d\nsum_cube=%d",

cube_sum(x,y),

printf("\n

sum_cube(x,y)); /*про функцію printf

див. підрозд. 3.8.2*/

}

 

 

 

Програма складається з двох одиниць компіляції. Перша файл funcs.c, друга файл start.c. Можна було б вибрати простішу структу- ру програми й одразу описати функції у файлі start.c та отримати про- граму у вигляді однієї одиниці компіляції. Однак запропонована струк- тура гнучкіша. Вона дозволяє мінімальними зусиллями модифікувати програму, не змінюючи її загальної структури, а за необхідності додати нові функції для обробки чисел або замінити існуючі на інші.

У системі UNІX для препроцесування, компілювання, компонуван- ня й виконання програми prog потрібно видати дві команди мовного процесора:

%cc –o prog func.c start.c

%prog

276

Розділ ІІІ. МОВИ ПРОГРАМУВАННЯ С ТА С++

Перша команда компілює й компонує два вхідні файли в образ за- дачі з іменем prog, друга завантажує програму prog і виконує її. Якщо ввести числа 2.0 та 3.0, то на екрані отримаємо результат:

2.0 3.0 cube_sum=125 sum_cube=35

У сучасних ОС замість явного застосування інструкцій командного процесора використовують відповідні кнопки інтегрованого середо- вища розробки IDE

Заголовні файли. Прикл. 3.5 показує, як заголовні файли викори- стовуються для організації взаємодії різних частин програми. Основ- на задача h-файлів описати або попередньо задекларувати спільні для кількох одиниць трансляції (або програм) дані й функції з метою їх використання в програмі ще до повного опису.

Задають такі визначення оператори опису й декларації. Скорочено будемо називати їх описом і декларацією даних. Декларації функцій мають спеціальну назву прототипи.

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

Увага! Оператори декларації не пов'язують з іменем конкретний об'єкт у ОП ►

Загальний вигляд оператора декларації:

<оператор-декларації>::=[<клас_пам'яті>] [<кваліфікатор_типу>] <тип>\

<специфікатор-імені>{,<специфікатор-імені>}; <клас_пам'яті>::=extern| static|auto|register <кваліфікатор_типу>::=const | volatile | restrict <тип>::=<специфікатор_типу>

Специфікатори імені подають імена змінних (функцій) і додаткову інформацію про їхній тип:

<специфікатор-імені>::=<прямий-специфікатор> | <специфікатор-покажчик>

277

ПРОГРАМУВАННЯ

<прямий-специфікатор>::=<простий-специфікатор>| (<специфікатор-імені>) | <неповний-специфікатор-масиву>| <cпецифікатор-функції>

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

int n;

struct S {int counter; float a} x;

В обох випадках специфікатори цілого типу int і типу структури struct S {int counter; float a} повністю визначають тип змінних, тому для декларації відповідних змінних достатньо простих описува- чів ідентифікаторів n та x. Конкретна будова специфікаторів змін- них залежить від типу змінних і буде розглядатися окремо для кожно- го з відповідних типів.

Специфікатори типу надають інформацію про тип змінної. Як уже зазначалося, ця інформація може доповнюватися специфікато- ром змінної:

<специфікатор_типу>::=<специфікатор-зліченного-типу> <специфікатор_цілого_типу> <специфікатор_дійсного_типу> <специфікатор_типу_структур> <специфікатор_типу_об'єднання> <специфікатор_імені_typedef> <специфікатор_типу_void>

Специфікатори типу розглядатимемо в підрозділах, присвячених конкретним типам.

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

Увага! Діє правило:

Кожна константа, змінна чи функція програми:

1)мають бути описані оператором опису;

2)якщо вони не описані перед своїм першим застосуванням у тек- сті програми, то обов'язково мають бути перед цим задекларовані

278

Розділ ІІІ. МОВИ ПРОГРАМУВАННЯ С ТА С++

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

Клас пам'яті й час існування даних. Компілятор, аналізуючи ін-

формацію про програмні дані, розподіляє їх за класами пам'яті. Ста- ндарт C містить чотири класи пам'яті:

зовнішня (extern);

статична (static);

автоматична (auto);

регістрова (register).

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

З поняттям даного пов'язане таке поняття, як час існування в про- цесі виконання програми. Час існування даного це час існування в ОП об'єкта, що зберігає значення даного, тобто відрізки часу з момен- ту створення об'єкта в ОП до його руйнації.

Автоматичні дані розміщуються на стеку під час виклику функції й доступні тільки всередині неї, де вони описані. Після завершення виклику доступ до них припиняється. Тому час існування автоматич- них даних виклики функцій.

Регістрові змінні розміщуються на регістрах.

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

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

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

279

ПРОГРАМУВАННЯ

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

Таблиця 3.4. Правило умовчання для класів пам'яті

Розташування

Вид

Клас пам'яті за умовчанням

Верхній рівень

Усі

Extern

Заголовок функції

Усі

Відсутній

Усередині блока

Функції

Extern

Усередині блока

Решта

Auto

Кваліфікатори типу. Впливають на засоби доступу до змінної. Стандарт С89 визначає два кваліфікатори: const та volatile. У C99 з'являється третій restrict. Класифікатор типу передує специфіка- тору типу у визначенні даного.

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

Приклад 3.6. Опис константи й декларації параметра з класифіка- тором const:

const int a=10; /*ціла константа зі значенням 10*/

сhar *strcat (сhar *s1, const сhar *s2); /*символьний параметр

– константа*/

Дія функції strcat полягає в додаванні до рядка символів першого аргументу символів рядка другого аргументу. Зрозуміло, що перший рядок мусить змінитися, а от кваліфікатор const перед другим гаран- тує збереження його значення на виході з функції ■

Значення змінної з кваліфікатором const не може змінюватися в програмі, але може змінюватися внаслідок зовнішнього впливу. На- приклад, адресу глобальної змінної-константи можна передати у фу- нкцію ОС, що стежить за часом, і тоді змінна буде відображати сис- темний час. У цьому випадку її значення буде змінюватися примусо- во без участі операторів програми.

Такі подробиці важливі для компіляторів, тому що більшість із них автоматично оптимізує вирази, які не змінюють свої значення. Якщо у виразі присутня змінна, що не змінюється явно операціями при- своювання, проте змінюється поза програмою, то така оптимізація коду може виявитись некоректною. Кваліфікатор volatile запобігає

280