Зубенко, Омельчук - Програмування. Поглиблений курс
.pdfРозділ ІІІ. МОВИ ПРОГРАМУВАННЯ С ТА С++
/**/***/*"/****//*,
///**/*/***///*//**/?
7.Яким буде подання в пам'яті літералів і якою буде довжина цих подань:
"\t\077"; "abc'1234'/*sos!*/\v"; " \x2514 3";
"\\\"\nLF"?
8.Яка структура C-програм?
9.Яка роль функції main у C-програмі?
10.Що таке препроцесор?
11.Що таке підключення файлів?
12.Що таке препроцесорні константи?
13.Яка роль заголовних файлів?
14.Що таке роздільна компіляція?
15.У чому полягає різниця між деклараціями й описом даних?
16.Які є класи пам'яті змінних і функцій?
17.Що таке час існування даного?
18.Що таке визначальний опис даного?
19.Чим відрізняється прототип від опису функції?
20.Що таке виклик функції?
21.Навести склад X-фрейму даних, що обробляє модифіковане тіло функції.
22.Що таке модифіковане тіло функції?
23.Що таке ініціалізація даних?
24.Що таке функція з побічним ефектом? Навести свої прикла- ди таких функцій.
25.Що таке локальна змінна?
26.Що таке глобальна змінна?
27.Що таке формальні параметри? Яка область їхньої дії?
28.Яке значення має змінна і у виділених точках програми:
int i=0; main()
{ auto int i=1; /* 1 */ {int i=2; /* 2 */
{ i+=1; /* 3 */
}
/* 4 */
}
/* 5 */
}?
29.Чи може дане у C-програмі бути одночасно і глобальним, і локальним?
30.Що виведуть такі фрагменти програм:
291
ПРОГРАМУВАННЯ
a) int f(int x, int y) |
б) int f(int x, int y) |
{ static int count=0; |
{static int count=0; |
count++; |
count++; |
printf("%d,",count); |
printf("%d,",count); |
return (x+y)/2; |
return |
} |
(x>0?f(x/2,y/2):1); |
main() |
} |
{ printf("%d\n",f(5,7)); |
main() |
printf("%d\n",f(25,13)); |
{ printf("%d\n",f(5,7)); |
printf("%d\n",f(1024,-40)); |
} ? |
} , |
|
|
|
3.2. Вирази
¾Структура виразів
¾Базові типи
¾Стандартні типи
¾Порядкові типи
¾Сумісність, зведення й узгодженість типів
Ключові слова: вираз, структура виразів, значення виразу, ліва й права асоці- ативність, l-вирази, r-вирази та f -вирази, вирази-присвоювання, таблиця операцій,
пріоритет операцій, унарні й бінарні арифметичні операції, унарні й бінарні логічні операції, базові й похідні типи мови С, стандартні змінні, арифметичні, символьні, булеві й порядкові змінні, умовні й послідовні вирази, тип void, сумісність і узгодже- ність типів, порядок на числових типах за діапазоном їхніх значень, зведення цілих і дійсних типів, зведення типів у операціях присвоювання, унарні й бінарні зведення, зведення фактичних параметрів функцій, операція примусового зведення типів.
Основою будь-якої мови є терми – конструкції, що подають реаль- ний чи абстрактний об'єкт. У мовах програмування терми назива- ються виразами. Вони є певним спеціалізованим варіантом Ω -термів. Як випливає з принципу типізації, мову програмування можна пода- ти у вигляді вежі типів зі структурою, яка описується об'єднаною Ω - системою її типів даних. Носій системи є універсумом усіх даних вежі типів, а сигнатуру Ω утворюють символи операцій та інтерфейсних і стандартних функцій, визначених на типах.
292
Розділ ІІІ. МОВИ ПРОГРАМУВАННЯ С ТА С++
Виразами мови C називаються Ω-терми її системи типів.
Щоб з'ясувати, що таке вираз мови C (і будь-якої іншої), необхідно й достатньо описати всю її вежу типів разом із сигнатурою. Усі типи вежі поділяються на базові й похідні (рис. 3.1). Базові містяться безпо- середньо в машинних командах. Для процесора ці дані є цілісними (неділимими) і твірними усієї системи даних. З іншого боку, базові типи є наближеними комп'ютерними моделями цілої, дійсної й ком- плексних арифметик. Це визначає їхню надзвичайно важливу роль у запитах вхідних систем.
У даному підрозділі ми розглянемо базові й стандартні типи (окрім типу покажчиків і комплексних чисел). Комплексні типи (а їх шість різновидів) передбачені лише в С99. Комплексним змінним відпові- дають об'єкти зі значеннями – двокомпонентними полями одного із трьох дійсних типів. Їхній опис і арифметику можна знайти в [133]. Решту похідних типів вивчатимемо в наступних підрозділах.
Спочатку конкретизуємо й розглянемо детальніше структуру вира- зів, а потім перейдемо до типів.
3.2.1. СТРУКТУРА ВИРАЗІВ
Вирази будуються з первинних виразів і викликів функцій за до- помогою операцій. Усі операції разом з їхньою сигнатурою й пріори- тетами наведено в табл. 3.5.
Як і у ПЧП, операції мають пріоритет і підлягають правилу асоціа- тивності (для бінарних операцій). Якщо порядок виконання операцій не визначається дужками, то операнди додаються до знаків операцій із вищим пріоритетом. Найвищим пріоритетом є 1.
Якщо дві операції мають однаковий пріоритет, то операнди приєд- нуються до них залежно від напрямку асоціативності. Розрізняють ліву й праву асоціативність. Якщо вираз a •b oc означає (a •b)oc ,
то говорять про ліву асоціативність, а якщо a •(b oc ) – то про праву. Правило асоціативності можна узагальнити:
Усі бінарні С-операції, за винятком присвоювань, мають ліву асоці- ативність.
Для унарних операцій діє правило:
Постфіксні унарні С-операції мають вищий пріоритет, ніж префі- ксні унарні. Послідовності унарних операцій без дужок обчислюються справа наліво.
293
ПРОГРАМУВАННЯ
Усі вирази мови С розподіляються на три види: l-, r- та f -вирази.
l - та r -вирази – це конструкції, що за синтаксисом можуть бути відповідно лівим і правим операндами в операції присвоювання (див. підрозд. 2.1.2). f -виразами називаються конструкції, які використо-
вуються як імена функцій (англ. – function designator). Вони розгля- даються в підрозд. 3.6.1.
С-вирази = l -вирази+ r -вирази+ f -вирази
|
|
|
|
|
|
Типи даних |
|
|
|
|
|
|
|
|
|
||||
|
|
|
|
|
|
|
мови С |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Базові |
|
|
|
|
|
|
|
|
|
|
Похідні |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Цілі |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
||
|
|
|
Числові |
|
|
|
|
|
|
|
|
|
|
Стандартні |
|
||||
|
|
|
|
|
|
|
Дійсні |
|
|
|
|
|
|
|
|
||||
|
|
|
Символьні |
|
|
|
|
|
|
|
Порядкові |
|
|||||||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
||||||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Комплексні |
|
|
|
|
|
|
|
|
|
|
Масиви |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
||
|
|
|
Адреси |
|
|
|
|
|
|
|
|
|
|
Структури |
|
||||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
||
|
|
|
Тип void |
|
|
|
|
|
|
|
|
|
|
Об’єднання |
|
||||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Бітові поля |
|
|
|||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Покажчики |
|
||||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Потоки |
|
|
|
|
|
|
|
|
|
|
|
|
Рис. 3.1 |
|
|
|
|
|
|
|
|||||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
||||||
|
|
|
Таблиця 3.5. Операції мови C |
|
|
|
|
||||||||||||
|
|
|
|
|
|
|
|
Асоціативність |
|||||||||||
Пріоритет |
|
|
Знак операції |
|
|
Тип операції |
|||||||||||||
1 |
() |
виклик функції |
|
|
|
|
|
|
|
Ліва |
|||||||||
|
[] |
індексація |
|
|
|
|
|
Бінарна |
|
|
|
||||||||
|
. |
|
взяття поля |
|
|
|
|
|
Бінарна |
|
|
|
|
|
|
||||
|
-> розіменування поля |
|
Бінарна |
|
|
|
|
|
|
294
Розділ ІІІ. МОВИ ПРОГРАМУВАННЯ С ТА С++
|
|
|
|
Закінчення табл. 3.5. |
Пріоритет |
|
Знак операції |
Тип операції |
Асоціативність |
2 |
~ побітове заперечення |
Унарні |
Права |
|
|
! |
заперечення |
- |
|
|
* розіменування |
- |
|
|
|
& взяття адреси |
- |
|
|
|
+ + збільшення |
- |
|
|
|
-- зменшення |
- |
|
|
|
+, - |
- |
|
|
|
(тип) зведення типу |
- |
|
|
|
sizeof розмір типу |
- |
Ліва |
|
3 |
* |
множення |
Мультипліка- |
|
|
/ |
ділення |
тивні |
|
|
% залишок |
|
|
|
4 |
+, - |
Адитивні |
|
|
|
|
|
|
|
5 |
<< зсув ліворуч |
Побітові |
|
|
|
>> зсув праворуч |
|
|
|
6 |
<, >, <=, >= |
Відношення |
|
|
7 |
== рівність |
Відношення |
|
|
|
!= |
нерівність |
|
|
8 |
& |
кон'юнкція |
Побітова |
|
9 |
^ додавання за mod 2 |
Побітова |
|
|
10 |
| диз'юнкція |
Побітова |
|
|
11 |
&& кон'юнкція |
Логічна |
|
|
12 |
|| диз'юнкція |
Логічна |
|
|
13 |
?: умовна |
|
Права |
|
14 |
=, *=, /=, %=, +=, -=, &=, |
Присвоювання |
||
|
|=, >>=, <<=, ^= |
|
|
|
15 |
, послідовне обчислення |
|
Ліва |
|
|
|
|
|
|
Приклад 3.15. Пріоритет і асоціативність операцій.
Вираз |
Еквівалентний вираз |
Коментар |
a+b/c |
a+(b/c) |
Мультиплікативна |
a*=b=c |
a*=(b=c) |
Права асоціативність |
a-77+b |
(a-77)+b |
Ліва асоціативність |
sizeof(double)*4 |
(sizeof(double))*4 |
Пріоритет sizeof вищий |
*p->q |
*(p->q) |
Пріоритет -> вищий |
*x++ |
*(x++) |
Тут ++ – постфіксна й унарна |
**x**y |
(*(*x))*(*y) |
Пріоритет розіменування вищий |
**x++++*y |
(*(*((x++)++)))*y |
Тут ++ – постфіксна й має най- |
|
|
вищий пріоритет, а унарна * |
|
|
вища, ніж бінарна * |
■ |
|
|
295
ПРОГРАМУВАННЯ
l-вирази:
<l-вираз>::=<змінна-стандартного-типу>|<індексований-вираз>| <вибір-компонентів>|<змінна-покажчик>| <розіменування-покажчика>|(<l-вираз>)
Значеннями l -виразів є адреси об'єктів певного типу з рівнем до- ступу до запису або до запису й читання. Однак потрібно взяти до уваги, що в деяких контекстах ці об'єкти не допускають запису. Не є l -виразами змінні з кваліфікатором const, імена масивів, функцій тощо. У табл. 3.6 перелічені всі l -вирази мови C.
l -вираз e
(e )
e.v
e →v
*e
e [i]
Таблиця 3.6. l -вирази мови C
Значення |
Коментар |
|
Адреса змінної e |
e – стандартного типу |
|
Значення виразу e |
e – l -вираз |
|
Адреса поля v |
e – структура |
|
Адреса поля v |
e – покажчик на структуру |
|
Значення розіменованої змінної |
e – покажчик |
|
Значення виразу * (e + i ) |
e – покажчик |
|
До операцій, що вимагають операндів – l -виразів, належать: взят- тя адреси &, збільшення + + , зменшення – і присвоювання.
Змінні стандартного типу розглядаються в підрозд. 3.2.3, індексо- вані вирази й вибір компонентів – у підрозд. 3.4, 3.7, присвячених масивам і структурам, змінні-покажчики й розіменування покажчи- ків – у підрозд. 3.5.
r -вирази – найчисленніший за кількістю операцій і найскладні- ший вид виразів. Нагадаємо (див. підрозд. 2.1.2), що r -вирази нале- жать до X -арних U -функцій, які виробляють значення змінних. Оскільки всі змінні типізовані, то й значення кожного r -виразу типі- зовані, тобто належать одному з типів. Цей тип називається типом r -виразу (див. підрозд. 3.2.4).
<r-вирази>::=<первинний-вираз>|<вираз-присвоювання>| <постфіксний-вираз>|<унарний-вираз> |<бінарний-вираз>|<логічний-вираз>|<умовний-вираз>| |<послідовний-вираз>|<константний-вираз>
Розглянемо деякі види r-виразів. Унарні, бінарні й логічні вирази описуються в підрозд. 3.2.3.
296
Розділ ІІІ. МОВИ ПРОГРАМУВАННЯ С ТА С++
З первинних виразів за допомогою операцій будуються всі решта:
<первинний-вираз>::=<l-вираз>|<константа>|(<вираз>)
Структуру l -виразів розглянуто вище. l -вирази як первинні віді- грають роль функцій читання, тому семантика їх залежить від кон- тексту. Діє правило: якщо наступною за l -виразом праворуч є опера- ція присвоювання, то l -вираз подає себе (тобто адресу), а якщо ні – то функцію читання. З операційного погляду функція читання повер- тає значення об'єкта, адресу якого задає l -вираз.
Приклад 3.16. Нехай int i – ціла змінна. Тоді в операції присвою- вання i=i+2 ліворуч ім'я i подає l -вираз, а праворуч – функцію чи- тання. Якщо значенням змінної i в полі даних α є 5, то обчислення цього виразу на полі α приведе до значення 7 і заміни в ньому зна- чення змінної i на 7 ■
Вирази-константи – це лексеми-константи зі значеннями відповід- ного типу. Літерали при цьому мають тип символьного покажчика.
Тип виразу в дужках збігається з типом внутрішнього виразу. За допомогою дужок групують внутрішні підвирази для зміни стандарт- ного порядку операцій або поліпшення читабельності виразів.
Приклад 3.17. Константи й вирази в дужках.
Нехай double a=1.0, b=-2.5; – опис дійсних змінних з ініціалізацією.
Вираз |
Тип |
Значення |
|
33 |
unsigned int |
33 |
|
0777 |
unsigned int |
51110 |
|
‘a' |
unsigned char |
97 |
|
0L |
long |
0 |
|
3.1428 |
double |
3.1428 |
|
"3.1428" |
*char |
адреса символу |
|
(a+b/2) |
double |
-0.25 |
|
((a+b)/2) |
double |
-0.75 |
■
Вирази присвоювання оновлюють значення змінних:
<присвоювання>::=<l-вираз> <операція-присвоювання> <r-вираз> <операція-присвоювання>::= =|*=|/=| %= |+=|-=|&=|
=|>>=|<<=|^=.
Вираз простого присвоювання e1 = e2 – це підстановки в операцію присвоювання на місце першого й другого операндів l - та r -виразів e1 та e2, типи яких збігаються або узгоджені. Узгодженими є всі арифме- тичні типи. Узгодженість решти типів розглядається у відповідних під-
297
ПРОГРАМУВАННЯ
розділах. Семантику операції присвоювання описано в підрозд. 2.1.2, а семантика виразу-присвоювання – це семантика складеної функції:
1)спочатку обчислюються значення виразів e1 та e2;
2)за несумісності типів e1 та e2 значення e2 зводиться до типу e1;
3)отримані значення підставляються в операцію присвоювання на місце відповідних аргументів.
Про сумісність і зведення типів йдеться в підрозд. 3.2.4. Семантика складеного присвоювання ‘О=', де О – арифметична операція, зводить- ся до випадку простого присвоювання за формулою e1 = e1Oe2 .
Постфіксні вирази:
<постфіксний-вираз>::=<вираз-збільшення>|<вираз-зменшення> <вираз-збільшення>::=<l-вираз>++ <вираз-зменшення>::=<l-вираз>--
Типи й значення виразів e + + та e − − збігаються й дорівнюють значенню виразу e .
Суть цих операцій у побічному ефекті – вони додають (віднімають) 1 до (від) значення e виразу після операції. У випадку покажчиків це означає їхнє пересування до наступного (попереднього) об'єкта в ОП даного типу.
Виклики функцій розглядаються в підрозд. 3.3.2, 3.6.2.
Умовні |
вирази |
є варіантом умовних термів і мають вигляд |
e ?e1: e2, |
де e – |
цілий вираз. Значення виразу задається умовним |
термом (e ≠ 0 → e1|e2).
Послідовні вирази мають вигляд e1,e2 . Семантика виразу полягає в послідовному обчисленні зліва направо виразів e1,e2 . Значення e1 відкидається, а значення e2 є значенням усього виразу. Операція асоціативна, тому можна послідовно об'єднувати в один єдиний дові- льну кількість виразів.
Константні вирази – це вирази, які в процесі обробки компілятором замінюються на значення-константи. До них належать препроцесорні константні вирази в директивах #if та #еlif, цілі константні вирази, що використовуються при описі границь масивів, значень зліченних типів тощо та ініціалізаційні константні вирази в операторах опису змінних.
3.2.2. БАЗОВІ ТИПИ
Мова C містить кілька варіантів кожного з базових типів, але опе- раціями вони не відрізняються. Базові цілі типи зображені в мові C цілими константами й арифметичними операціями. Останні наведені в табл. 3.5 і розглядаються в підрозд. 3.2.3. Визначено кілька цілих
298
Розділ ІІІ. МОВИ ПРОГРАМУВАННЯ С ТА С++
типів різних розмірів. Константи цілого типу можуть записуватись у десятковій, вісімковій і шістнадцятковій системах із суфіксами u (U) для беззнакових цілих, l(L), ll(LL) – для довгих цілих:
<ціла-константа>::=<десяткова-константа> <суфікс-цілого>| |<вісімкова-константа> <суфікс-цілого>| |<шістнадцяткова-константа> <суфікс-цілого>
<десяткова-константа>::=<ненулева-цифра>{<цифра10 >} <вісімкова-константа>::=0{<цифра8 >}
<шістнадцяткова-константа>::=(0x|0X)<цифра16 >{<цифра16 >}
<суфікс-цілого>::=<cуфікс-беззнакового>[<суфікс-довгого>] | |<суфікс-довгого>[<cуфікс-беззнакового>] <cуфікс-беззнакового>::=u | U
<суфікс-довгого>::=l | L| ll| LL
Для визначення основи системи числення цілої константи необхід- но послідовно виконати п. 1-3 такого правила:
1)Якщо константа має один із префіксів 0x, 0X, то вона шістна- дцяткова.
2)Якщо константа має префікс 0, то вона вісімкова.
3)Це десяткова константа?
Зазначимо, що для отримання від'ємного цілого необхідно застосу- вати до нього операцію унарний мінус. Базовим цілим відповідають об'єкти ОП, в яких зберігаються на машинному рівні цілі числа дано- го діапазону. Зазвичай вони зберігаються у форматі двійкових обер- нених кодів (див. підрозд. 2.3.3). Фактичний діапазон типу цілих кон- стант може залежати від їхнього розміру, системи числення, суфіксів і внутрішнього подання, вибраного компілятором (табл. 3.7).
Приклад 3.18. Цілі константи: 10U – десяткове беззнакове ціле; 25uL – десяткове беззнакове довге; 1LL – десяткове довге-довге; 0L – вісімковий довгий нуль 08 ; 037 – вісімкове; -0777 – від'ємне вісімкове
-7778 ; -0x25 – від'ємне шістнадцяткове - 2516 ■
УС99 дійсні константи записуються або за допомогою мантиси з точкою (не обов'язково нормалізованою), або цілого з порядком, або мантиси з порядком:
<дійсна-константа>::=<десяткова-дійсна-константа> | <шістнадцяткова-дійсна-константа> <десяткова-дійсна-константа>::=
299
ПРОГРАМУВАННЯ
<десяткова-константа-з-0> <експонента> [<суфікс-дійсного>] | <дійсне-з-фіксованою-точкою>[<експонента>][<суфікс-дійсного>] <десяткова-константа-з-0>::=<десяткова-константа> | 0 <дійсне-з-фіксованою-точкою >::=[<десяткова-константа-з-0>]. \ [<десяткова-константа-з-0>] <експонента>::=(e|E)[<знак>]<десяткова-константа> <знак>::=+|-
<cуфікс-дійсного>::=f|F|l|L
У числах із фіксованою точкою не можуть бути одночасно відсутні й ціла, і дробова частини. Суфікси виділяють константи типів float та long double. Відсутність суфікса означає, що тип константи – double.
Для отримання від'ємної дійсної константи необхідно, як і у випад- ку цілих, застосувати операцію унарний мінус.
Три дійсні типи відрізняються тільки точністю й розміром порядку. Якщо точність чисел не принципова й необхідно економити пам'ять (напр., у таблицях), то використовують тип float.
Приклад 3.19. Десяткові дійсні константи 0.f(=0.0), 1.0f(=1.0),
.25 f(=0.25) належать до типу float, константа 1e-2L (=0.01) – до
типу long double, константа 1.0e+25 (=10 25 .0) – до типу double ■
Якщо компілятор не може подати дану дійсну константу точно, то він вибирає найближче до неї значення, яке може подати. Якщо дане дійсне більше найбільшого чи менше найменшого значення в дійсно- му типі, то результат буде непередбачуваний.
Як і у випадку базових цілих, базовим дійсним відповідають об'єк- ти ОП, в яких зберігаються на машинному рівні дійсні числа даного діапазону (див. підрозд. 2.3.3). Фактичний їхній діапазон, щільність, кількість цифр у мантисі й порядку залежать від компілятора.
У С99 передбачено заголовний файл float.h, в якому розміщують- ся граничні значення для дійсних констант у даному компіляторі. У табл. 3.7 наведено деякі з них.
На відміну від стандарту С89, стандарт С99 дозволяє використову- вати також шістнадцяткові дійсні константи. Формат схожий на фо- рмат десяткових дійсних. Таким константам передує шістнадцятко- вий префікс 0x, 0X. Експонентою є двійкова експонента вигляду
p ±n16 зі значенням 2n , де n =n16 .
Приклад 3.20. Шістнадцяткові дійсні константи:
0X1.0(=1.0), 0x19.(=25.0), 0XAp-2(10*2 −2 =2.5) ■
300