- •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 Приклад - розподільник пам'яті
3.6 Цикли do-while
Як ми вже обговорили в Розділі 1, цикли while і for перевіряють умову завершення циклу на самому початку. На противагу першим двом, do-while перевіряє умову вкінці, після проходження кожного кроку у корпусі цикла; корпус завжди виконується щонайменше один раз.
Синтаксис do наступний:
do
твердження
while (вираз)
Тож, спочатку буде виконано твердження, і лиш потім оцінено вираз. Якщо умова справджується, твердження виконується ще раз, і так далі... Коли вираз виявиться хибним, цикл буде перервано. За винятком змісту перевірки, do-while еквівалентний repeat-untill у Pascal.
Досвід свідчить, що do-while використовується значно рідше ніж while або for. Тим не менше, час од часу він - корисний, як у наступній функції itoa, яка перетворює число у символьний ланцюжок (функція, протилежна atoi). Ця задача трохи складніша, ніж може видатись з першого погляду, оскільки легкі способи генерації чисел розставляють ці числа в невірному порядку. Ми вирішили відтворити ланцюжок в оберненому порядку, а потім розвернути його.
/* itoa: перетворює n у знаки s */
void itoa(int n, char s[])
{
int i, sign;
if ((sign = n) < 0) /* виявити знак */
n = -n; /* перетворити n на додатнє */
i = 0;
do { /* генерувати цифри в зворотньому порядку */
s[i++] = n % 10 + '0'; /* одержати наступну цифру */
} while ((n /= 10) > 0); /* видалити її */
if (sign < 0)
s[i++] = '-';
s[i] = '\0';
reverse(s);
}
Цей do-while — потрібний, або щонайменше зручний, оскільки принаймні один знак повинен потрапити в масив s, навіть якщо n буде нульовим. Ми використали фігурні дужки навколо єдиного твердження з якого складається корпус do-while (хоча вони не обов'язкові) для того, щоб поспішний читач не сприйняв while-частину за початок циклу while.
Вправа 3-4. У двійковому взаємодоповнюючому представленні чисел, наша версія itoa не справляється з найбільшим від'ємним числом, тобто значенням n що дорівнює -(2величина слова-1). Поясніть чому ні. Поміняйте програму, щоб вона виводила це число правильно, незалежно від машини на якій вона виконується.
Вправа 3-5. Напишіть функцію itob(n,s,b) яка переводить десяткове n у b систему числення і зберігає це у рядку s. Так, наприклад, itob(n,s,16) сформує n як шістнадцяткове число, збережене в ланцюку s.
Вправа 3-6. Напишіть версію itoa, яка братиме три аргументи замість двох. Третім аргументом буде мінімальна ширина поля. Конвертоване число буде доповнюватись пробілами зліва у разі потреби, щоб добитися певної ширини.
3.7 Break і continue
Часом зручно мати можливість вийти з циклу інакше, ніж шляхом тестування умови зверху або внизу. Саме твердження break забезпечує передчасний вихід із for, while або do-циклів, так само як і у випадку зі switch. Вираз break змушує найближчий цикл або switch, у якому він знаходиться, завершитись негайно.
Наступна функція, яку ми назвали trim, вилучає кінцеві пробіли, кроки табуляції та символи нового рядка в ланцюжкові, використовуючи break для виходу з циклу, як тільки знайдено перший знак, що не є ні пробілом, ні знаком табуляції, ні символом нового рядка.
/* trim: видаляє хвостові пробіли, табуляцію і символи *
* нового рядка */
int trim(char s[])
{
int n;
for (n = strlen(s)-1; n >= 0; n--)
if (s[n] != ' ' && s[n] != '\t' && s[n] != '\n')
break;
s[n+1] = '\0';
return n;
}
strlen повертає довжину ланцюжка. Цикл for починає з кінця ланцюжка і перевіряє у зворотньому напрямку, шукаючи перший знак, що не являється пробілом, табуляцією або символом нового рядка. Цикл перерветься, коли один з них буде знайдено або, коли n стане від'ємним (тобто, коли весь ланцюжок пройде перевірку). Вам слід перевірити, чи цей цикл веде себе правильно, навіть коли ланцюжок порожній або містить тільки символи пропуску.
Оператор continue — споріднений з break, але не так часто використовується. Він зумовлює початок наступної ітерації оточуючого циклу for, while або do. У випадку while і do, тестова частина виконується негайно; у випадку for, керування передається етапові приросту. Твердження continue вживається тільки з циклами, а не switch-конструкціями. Операторcontinue всередині switch, розміщеному в циклі, спричинить наступну ітерацію циклу.
Як приклад, наступний фрагмент коду обробляє тільки додатні елементи масиву a, пропускаючи від'ємні.
for (i = 0; i < n; i++) {
if (a[i] < 0) /* пропустити від'ємні елементи */
continue;
... /* обробити додатні елементи */
}
Твердження з continue часто використовуються, коли наступна частина циклу занадто складна, тож перевірка протилежної умови та додатковий відступ коду вправо гніздить програму занадто глибоко.
