
- •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 Приклад - розподільник пам'яті
5.7 Багатовимірні масиви
C надає прямокутні багатовимірні масиви, хоча на практиці вони набагато рідше вживані ніж масиви покажчиків. В цьому розділі ми продемонструємо деякі з їхніх властивостей.
Уявіть собі задачу по перетворенню дати з дня місяця в день року і навпаки. Наприклад, 1-го березня — це 60-ий день звичайного року і 61-ий день високосного. Давайте визначимо дві функції для здійснення перетворення: day_of_year перекладає місяць і день у день року, іmonth_day, яка переводить день року в місяць і день. Так як остання функція обчислює два значення, аргументи місяця і дня будуть покажчиками:
month_day(1988, 60, &m, &d)
присвоїть m значення 2, а d значення 29 (29-го лютого).
Обидві ці функції потребують тієї самої інформації — таблиці з кількістю днів у кожному місяці («рівно 30 днів у вересні ...»). Так як кількість днів відрізняється в високосному і невисокожному році, буде легше розділити їх на два ряди у двохвимірному масиві, після чого звертати увагу на лютий під час обчислень. Масив і функції для здійснення перетворень слідують:
static char daytab[2][13] = {
{0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
{0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
};
/* day_of_year: отримати день року маючи місяць і день */
int day_of_year(int year, int month, int day)
{
int i, leap;
leap = year%4 == 0 && year%100 != 0 || year%400 == 0;
for (i = 1; i < month; i++)
day += daytab[leap][i];
return day;
}
/* month_day: отримати місяць і день з дня року */
void month_day(int year, int yearday, int *pmonth,
int *pday)
{
int i, leap;
leap = year%4 == 0 && year%100 != 0 || year%400 == 0;
for (i = 1; yearday > daytab[leap][i]; i++)
yearday -= daytab[leap][i];
*pmonth = i;
*pday = yearday;
}
Якщо пригадуєте, арифметичним значенням логічного виразу, як у випадку leap (англ. високосний), може бути або нуль (хибно), або один (істина), тож це значення можна також використати як індекс масиву daytab.
Масив daytab повинен бути зовнішнім для обох, day_of_year і month_day, щоб обидва могли користуватись ним. Ми оголосили масив як char для ілюстрації того як використовуватиchar для збереження малих незнакових цілих.
daytab — це перший двовимірний масив з яким ми досі стикалися. У C, двовимірний масив, це насправді одновимірний, кожен елемент якого також являється масивом. Тому індекси записуються як
daytab[i][j] /* [рядок][стовпчик] */
замість
daytab[i,j] /* НЕПРАВИЛЬНО */
За винятком цієї різниці в нотації, двовимірні масиви можна трактувати майже так само як і в інших мовах. Елементи зберігаються рядами, тож індекс з правого боку (або стовпчик), змінюється найшвидше, коли до елементів звертаються в послідовності, в які їх збережено.
Такий масив ініціалізується за допомогою, включеного у фігурні дужки, списку ініціалізаторів; кожний рядок двовимірного масиву ініціалізується відповідним другорядним списком. Ми почали масив daytab зі стовпчика, який містить 0, тож цифри місяців можна відраховувати природнішим 1 до 12, замість 0 до 11. Оскільки неможливо використати пропуск у цій ситуації, це зрозуміліше ніж пізніша зміна індексів.
При передачі двовимірного масиву як аргумент функції, оголошення параметра функції повинно включати кількість стовпчиків; кількість рядків не є важливим так як те, що передається, як ми це раніше засвоїли, це покажчик на масив рядків, кожен з яких містить масив з 13-ти елементів типу int. У цьому конкретному випадку, це покажчик на об'єкти, що являють собою масиви з 13-и int. Таким чином, якщо масив daytab передати функції f, оголошенням f буде:
f(int daytab[2][13]) { ... }
Також це могло би бути
f(int daytab[][13]) { ... }
так як кількість рядків не є важливою, або це могло б також бути
f(int (*daytab)[13]) { ... }
що вказує на те, що параметр — це покажчик на масив з 13-и цілих. Дужки потрібні, оскільки квадратні дужки [] мають більший пріоритет за *. Без дужок оголошення
int *daytab[13]
буде масивом з 13 покажчиків на цілі int. Як узагальнення, тільки перший вимір (індекс) масиву є вільним, решту потрібно вказувати. Розділ 5.12 включає подальше обговорення складних оголошень.
Вправа 5-8. Перевірки на помилки в day_of_year і month_day бракує. Виправіть цей недолік.