- •1.1 Перші кроки
- •1.2 Змінні й арифметичні вирази
- •1.3 Твердження for
- •1.4 Символічні константи
- •1.5 Ввід і вивід знаків
- •1.5.1 Копіювання файла
- •1.5.2 Відлік символів
- •1.5.3 Відлік рядків
- •1.6 Масиви
- •1.7 Функції
- •1.8 Аргументи - виклик за значенням
- •1.9 Символьні масиви
- •1.10 Зовнішні змінні й область дії
- •2.1 Назви змінних
- •2.2 Типи даних і розміри
- •2.3 Константи
- •2.4 Оголошення
- •2.5 Арифметичні операції
- •2.6 Реляційні та логічні оператори
- •2.7 Перетворення типів
- •2.8 Оператори приросту та спаду
- •2.9 Розрядні оператори
- •2.10 Оператори та вирази присвоєння
- •2.11 Вирази умов
- •2.12 Пріоритет і послідовність обчислення
- •3.1 Вирази та блоки
- •3.3 Else if
- •3.4 Switch
- •3.5 Цикли while та for
- •3.6 Цикли do-while
- •3.7 Break і continue
- •3.8 Goto та мітки
- •4.1 Основні знання про функції
- •4.2 Функції, які не повертають цілих
- •4.3 Зовнішні змінні
- •4.4 Правила області дії
- •4.5 Файли заголовка
- •4.6 Статичні змінні
- •4.7 Регістрові змінні
- •4.8 Структура блоків
- •4.10 Рекурсія
- •4.11 Препроцесор c
- •4.11.1 Включення файлів
- •4.11.2 Заміна макросів
- •4.11.3 Обумовлене включення файлів
- •5.1 Покажчики й адреси
- •5.2 Покажчики й аргументи функцій
- •5.3 Покажчики та масиви
- •5.4 Арифметика адрес
- •5.5 Покажчики на символи та функції
- •5.6 Масив покажчиків; покажчики на покажчики
- •5.7 Багатовимірні масиви
- •5.8 Ініціалізація масиву покажчиків
- •5.9 Покажчики в порівнянні з багатовимірними масивами
- •5.10 Аргументи командного рядка
- •5.11 Покажчики на функції
- •5.12 Складні оголошення
- •6.1 Основні поняття про структури
- •6.2 Структури та функції
- •6.3 Масиви структур
- •6.4 Покажчики на структури
- •6.5 Структури зі зворотнім звертанням
- •6.6 Пошук по таблиці
- •6.7 Typedef
- •6.8 Сполуки
- •6.9 Розрядні поля
- •7.1 Стандартний ввід і вивід
- •7.2 Форматований вивід - printf
- •7.3 Списки аргументів довільної довжини
- •7.4 Форматований ввід - scanf
- •7.5 Доступ до файлів
- •7.6 Обробка помилок - stderr і exit
- •7.7 Ввід і вивід рядків
- •7.8 Додаткові функції
- •7.8.1 Операції з ланцюжками
- •7.8.2 Перевірка і перетворення класів символів
- •7.8.3 Ungetc
- •7.8.4 Виконання команд
- •7.8.5 Керування пам'яттю
- •7.8.6 Математичні функції
- •7.8.7 Генератор випадкових чисел
- •8.1 Дескриптори файлів
- •8.2 Низькорівневий ввід/вивід - read і write
- •8.3 Open, creat, close, unlink
- •8.4 Довільний доступ - lseek
- •8.5 Приклад: втілення fopen і getc
- •8.6 Приклад - перелік вмісту каталогів
- •8.7 Приклад - розподільник пам'яті
2.10 Оператори та вирази присвоєння
Вираз на кшталт
i = i + 2
в якому змінна з лівого боку одразу повторюється на правому, можна записати в стислішій формі
i += 2
Оператор += також називається оператором присвоєння. Більшість бінарних операторів (такі як +, що має лівий і правий операнд) мають відповідний оператор присвоєння op=, де op може бути одним з наступних
+ - * / % << >> & ^ |
Якщо expr1 і expr2 — це два вирази, тоді
expr1 op= expr2
еквівалентний
expr1 = (expr1) op (expr2)
крім випадку, коли expr1 обчислюється тільки один раз. Зверніть увагу на дужки навколо expr2:
x *= y + 1
означає
x = x * (y + 1)
замість
x = x * y + 1
В якості прикладу наведемо функцію bitcount, яка обчислює кількість 1-бітів в своєму аргументі-цілому.
/* bitcount: рахує 1-біти в x */
int bitcount(unsigned x)
{
int b;
for (b = 0; x != 0; x >>= 1)
if (x & 01)
b++;
return b;
}
Оголошення аргументу x як unsigned (беззнакового) гарантує, що коли його буде зміщено вправо, звільнені біти заповняться нулями, а не знаковими бітами, незалежно від машини на якій було запущено програму.
Крім стислості, оператори присвоєння мають переважають тим, що вони краще відповідають способу мислення людей. Ми кажемо «додати 2 до i» або «збільшити i на 2», а не «взяти i, додати 2, а потім помістити результат назад у i». Тому виразу i += 2 надається перевага над i = i+2. На додаток, у складних виразах на зразок
yyval[yypv[p3+p4] + yypv[p1]] += 2
оператор присвоєння полегшує розуміння коду, оскільки читач не змушений ретельно перевіряти, що два довгих вирази дійсно відповідають один одному, або дивуватися, чому ні. Оператор присвоєння, може навіть допомогти компілятору виробити ефективніший код.
Ми вже бачили, що твердження присвоєння повертає певне значення і може вживатися у більших виразах; найтиповішим прикладом може бути
while ((c = getchar()) != EOF)
...
Інші оператори присвоєння (+=, -= тощо) також можуть зустрічатися всередині виразів, хоч і не так часто.
В усіх таких виразах, тип виразу присвоєння збігається з типом лівого операнду, а значення відповідає значенню після присвоєння.
Вправа 2-9. У двійковій числовій системі (two's complement number system), x &= (x-1)вилучає крайній правий біт з x. Поясніть чому. Скористайтеся з цього спостереження для написання швидшої версії bitcount.
2.11 Вирази умов
Вирази на зразок
if (a > b)
z = a;
else
z = b;
обчислюють z як найбільше значення з-поміж a та b. Умовний вираз із використанням трійчастого оператора «?:» дають альтернативний спосіб написання подібної конструкції. У виразі
expr1 ? expr2 : expr3
вираз expr1 розглядається першим. Якщо expr1 ненульовий (істина), тоді обчислюється expr2, і це й буде кінцевим значенням цілого виразу умови. У протилежному випадку, обчислюється expr3, і він стане кінцевим значенням. Оцінюється тільки один із expr2 та expr3. Таким чином, щоб встановити z до максимального значення a або b, ми напишемо
z = (a > b) ? a : b; /* z = max(a, b) */
Слід зауважити, що вираз умови — це дійсно вираз, і може бути використаний там де будь-який інший. Якщо expr2 і expr3 — різних типів, тип результату визначається правилами перетворення, обговорених раніше в цьому розділі. Наприклад, якщо f має тип float, а n — це int, тоді вираз
(n > 0) ? f : n
буде типу float, незалежно від того, чи n додатній.
Дужки не обов'язкові навколо першого виразу оскільки пріоритет ? й : дуже низький, лишень трохи вищий за присвоєння. Проте, їх рекомендовано вживати, оскільки вони роблять умовну частину виразу очевиднішою.
Умовні вирази, як правило, призводять до стислішого коду. Наприклад, наступний цикл виводить n елементів масиву, по 10 на кожний рядок, стовпчики будучи розділені пробілом, кожний рядок (включаючи останній) завершується символом нового рядка.
for (i = 0; i < n; i++)
printf("%6d%c", a[i], (i%10==9 || i==n-1) ? '\n' : ' ');
Символ нового рядка виводиться після кожного десятого елемента і після n-нного. За рештою елементів слідує пробіл. Це може здаватися складним, але воно компактніше ніж еквівалент ізif-else. Іншим хорошим прикладом є
printf("You have %d items%s.\n", n, n==1 ? "" : "s");
Вправа 2-10. Перепишіть функцію lower, яка обертає літери верхнього регістру на нижній, за використанням виразу умови замість if-else.
