
- •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.4 Оголошення
Усі змінні потрібно оголосити до їхнього використання, хоч деякі оголошення можна зробити непрямо, через зміст. Оголошення вказує тип і містить список з однієї або більше змінних цього типу, як, наприклад
int lower, upper, step;
char c, line[1000];
Змінні можна розподілити поміж оголошень у будь-який спосіб; списки вище так само можна було б записати як
int lower;
int upper;
int step;
char c;
char line[1000];
Остання форма забирає більше місця, але вигідна тим, що дозволяє додати коментар до кожного оголошення, для пізніших змін.
Змінну можна також ініціювати (надати їй початкового значення) під час її оголошення. Якщо за назвою слідує знак рівності та вираз, цей вираз служитиме ініціалізатором, як, скажімо
char esc = '\\';
int i = 0;
int limit = MAXLINE+1;
float eps = 1.0e-5;
Якщо змінна не є автоматичною, ініціалізація відбудеться тільки один раз, за задумом — до того як програма почне своє виконання, але ініціалізатор має бути сталим виразом. Явно ініційована автоматична змінна, започатковується кожного разу при входженні у функцію або блок коду де вона знаходиться; ініціалізатор може складатися з будь-якого виразу. Зовнішні та статичні змінні, поза вибором, ініціалізуються до нуля. Автоматичні змінні, які не мають явного ініціалізатора, отримають невизначене значення (тобто непотріб).
Для вказівки того, що значення змінної не змінюватиметься, до оголошення можна додати класифікатор const. У випадку масивів, класифікатор const вказує на те, що елементи масиву залишатимуться незмінними.
const double e = 2.71828182845905;
const char msg[] = "warning: ";
Оголошення з const можуть також вживатися з масивами в якості аргументів, щоб вказати на те, що функція, скажімо, не змінює масиву:
int strlen(const char[]);
Якщо відбудеться спроба змінити значення змінної типу const, результат такої дії залежатиме від реалізації.
2.5 Арифметичні операції
Арифметичними операми з двома операндами є +, -, *, / і оператор коефіцієнту %. Поділ цілих відкидає дробову частину. Вираз
x % y
повертає залишок поділу x на y, і нуль, якщо ділення відбудеться без залишку. Наприклад, рік буде високосним, якщо він кратний 4, але не 100, за винятком того, що роки кратні 400 — теж високосні. Таким чином
if ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0)
printf("%d is a leap year\n", year);
else
printf("%d is not a leap year\n", year);
Оператор % неможливо застосувати із float та double. Напрямок округлення у випадку / і знак результату % є машинозалежним для від'ємних чисел, так само як дії у випадку втрати значності або переповнення. Оператори + та - рівні за пріоритетом, але мають менший пріоритет ніж *, / і %, які в свою чергу, поступаються унарним + та -. Арифметичні оператори діють з ліва на право.
Таблиця 2.1, у кінці цього розділу, підводить підсумок пріоритетів і асоціативності (спрягання) всіх операторів.
2.6 Реляційні та логічні оператори
Реляційними операторами є
> >= < <=
Вони всі мають однаковий пріоритет. Трохи нижче за пріоритетом знаходяться оператори рівності:
== !=
Реляційні оператори наділені меншим пріоритетом за арифметичні, тож вираз на зразок i < lim-1 розглядатиметься як i < (lim-1), як і очікувалось.
Цікавішими є логічні оператори && та ||. Вирази, поєднані && або ||, оцінюються з ліва на право, і їхня оцінка закінчується, як тільки виявлено істинність чи хибність результату. Більшість програм C покладаються на ці властивості. Ось, наприклад, цикл функції вводу, яку ми написали у Розділі 1:
for (i=0; i < lim-1 && (c=getchar()) != '\n' && c != EOF; ++i)
s[i] = c;
Перед тим як прочитати новий знак, необхідно перевірити, чи є місце для його збереження в масиві s, тож треба спочатку дізнатися, чи i < lim-1. Якщо ця перевірка зазнає невдачі, нам не слід продовжувати далі, і читати наступний знак.
Так само, було би недоцільно перевіряти c на предмет EOF (кінця файла), якщо до цього не викликано getchar; саме тому виклик і присвоєння мають відбутися до перевірки символу, збереженого в c.
Пріоритет && є більшим за ||, але обидва поступаються релятивним операторам і операторам рівності, тож вирази на зразок
i < lim-1 && (c=getchar()) != '\n' && c != EOF
не вимагають додаткових дужок. Але оскільки пріоритет != переважає оператор присвоєння =, дужки обов'язкові у випадку
(c=getchar()) != '\n'
для того, щоб добитися бажаного результату - спочатку, присвоєння значення c, а потім, порівняння його з '\n'.
За визначенням, числове значення релятивного або логічного виразу дорівнює 1, якщо співвідношення істинне, і 0 — якщо хибне.
Унарний оператор заперечення ! перетворює ненульовий операнд на 0 (нуль) і, навпаки, нульовий операнд на 1 (одиницю). Поширеним є застосуванням ! в конструкціях на зразок
if (!valid)
замість
if (valid == 0)
Важко узагальнити, яка з цих форм є кращою. Конструкції на кшталт !valid легко читаються («if not valid» — «якщо не дійсне»), але складніші вирази буває важко зрозуміти.
Вправа 2-2. Напишіть цикл, еквівалентний наведеному вище, але без використання && або ||.