
- •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 Приклад - розподільник пам'яті
1.5.2 Відлік символів
Наступна програма лічить символи; вона подібна до попередньої програми копіювання.
#include <stdio.h>
/* лічить символи вводу; 1-а версія */
main()
{
long nc;
nc = 0;
while (getchar() != EOF)
++nc;
printf("%ld\n", nc);
}
Вираз
++nc;
знайомить нас з новим оператором, ++, який означає «збільшити на одиницю». Ви могли би натомість написати nc = nc + 1, але ++nc стисліший і, часто, ефективніший. Існує також відповідний оператор -- для зменшення на одиницю. Оператори ++ й -- можуть бути префіксними (++nc) та постфіксними (nc++); ці дві форми мають різне значення у виразах, як буде показано в Розділі 2, але ++nc й nc++, обидва, збільшують nc на один. Для наших цілей, ми зупинимось на префіксній формі.
Програма відліку знаків зберігає кількість знаків у змінній типу long замість int. Довгі цілі мають, щонайменше, 32-бітну довжину. Хоч на деяких машинах int та long однакової довжини, на інших int має лише 16 біт, з максимальним значенням 32767, і потрібно дуже мало вводу, щоб переповнити int-лічильник. Вказівник перетворення %ld вказує printf, що відповідний аргумент є довгим цілим.
Ми можемо упоратись і з більшими числами, застосувавши тип double (число з рухомою точкою подвійної точності). Ми також використаємо твердження for замість while, для демонстрації іншого способу написання циклу.
#include <stdio.h>
/* лічить символи вводу; 2-а версія */
main()
{
double nc;
for (nc = 0; gechar() != EOF; ++nc)
;
printf("%.0f\n", nc);
}
printf використовує %f для обох типів, float і double; %.0f пригнічує вивід десяткової крапки та дробової частини, яку ми вказали як нуль.
Корпус цього циклу порожній, оскільки вся робота вже зроблена у тестовій та інкрементній частині циклу. Але граматичні правила C вимагають, щоб твердження for мало корпус. Поодинока крапка з комою, яку називають «нульовим твердженням» знаходиться там для того, щоб задовольнити це правило. Ми розмістили її на окремому рядку, щоб вона була помітною.
Перед тим як ми покинемо програму-лічильник, зверніть увагу, що якщо ввід не містить жодних знаків, тести while або for зазнають невдачі при першому ж викликові getchar і програма виведе нуль — правильне значення. Це важливо. Однією з гарних рис while або for є те, що вони здійснюють перевірку на вершечку циклу, до переходу до виконання самого корпусу. Якщо робити нічого не треба, нічого й не буде зроблено, навіть якщо це означає не входити жодного разу в корпус циклу. Програми мають поводитись розумно, коли їм надано ввід нульової довжини. Твердження while та for допомагають упевнитися, що програми здійснюють розумні речі з граничними умовами.
1.5.3 Відлік рядків
Наступна програма лічить введені рядки. Як ми згадали вище, стандартна бібліотека забезпечує тим, щоб потік ввідного тексту з'являвся як послідовність рядків, кожен з яких закінчується символом нового рядка. Тому відлік рядків — це просто підрахунок символів нового рядка:
#include <stdio.h>
/* лічить рядки вводу */
main()
{
int c, nl;
nl = 0;
while ((c = getchar()) != EOF)
if (c == '\n')
++nl;
printf("%d\n", nl);
}
Корпус while тепер включає умову if, яка, в свою чергу, керує приростом ++nl. Твердженняif перевіряє умову в дужках, і якщо вона істинна, виконує наступне твердження (або групу тверджень у фігурних дужках). Ми знову намагались показати, що контролюється чим.
Подвійний знак рівності == є нотацією C для «рівний з» (подібний до одного знака = Pascal або.EQ мови Fortran). Використовуються два символи рівності ==, щоб відрізнити перевірку на рівність від одного =, який у C означає присвоєння. Невеличке застереження: новачки C іноді пишуть = там, де вони мають на увазі ==. Як ми побачимо з Розділу 2, результат зазвичай складає чинний вираз, тож ви не отримаєте жодного попередження.
Символ в одинарних лапках повертає ціле, рівне числовому значенню знака в наборі символів машини. Це називається символьною константою (сталою), а насправді — просто інший спосіб написання невеличкого цілого. Таким чином, наприклад, 'A' — це символьна константа, значення якої дорівнює 65 в наборі символів ASCII, — внутрішньому представленні знака A. Звичайно 'A' надається перевага перед 65, оскільки значення першого очевидніше, і не залежить від певного набору знаків.
Дозволяється також використання екранованих послідовностей, як символьні константи, тож'\n' означає значення символу нового рядка, яке дорівнює 10 в ASCII. Вам слід звернути увагу на те, що '\n' — це єдиний знак і у виразах є просто цілим числом, з іншого боку, "\n" — це ланцюжкова константа, яка, так сталося, що містить тільки один знак. Тему ланцюжків у порівнянні із символами розглянуто далі у Розділі 2.
Вправа 1-8. Напишіть програму з підрахунку пробілів, табуляції та нових рядків.
Вправа 1-9. Напишіть програму, яка би копіювала свій ввід до виводу, замінюючи кожний ланцюжок з одного або більше пробілів на єдиний пробіл.
Вправа 1-10. Напишіть програму, яка би копіювала свій ввід до виводу, замінюючи кожну табуляцію на \t, кожний реверс на \b і кожну зворотню похилу на \\. Це зробить табуляцію і реверси видимими у недвозначний спосіб.
1.5.4 Відлік слів
Четверта, з нашого набору корисних програм, лічить рядки, слова та знаки, з приблизним визначенням, що слово — це будь-яка послідовність знаків, що не містить пробілів, табуляції або символу нового рядка. Це спрощена версія Юнікс-програми wc.
#include <stdio.h>
#define IN 1 /* всередині слова */
#define OUT 0 /* зовні слова */
/* лічить рядки, слова та знаки вводу */
main()
{
int c, nl, nw, nc, state;
state = OUT;
nl = nw = nc = 0;
while ((c = getchar()) != EOF) {
++nc;
if (c == '\n')
++nl;
if (c == ' ' || c == '\n' || c = '\t')
state = OUT;
else if (state == OUT) {
state = IN;
++nw;
}
}
printf("%d %d %d\n", nl, nw, nc);
}
Кожний раз, як програма зустрічає перший знак слова, вона додає до рахунку ще одне слово. Змінна state занотовує, чи програма у дану мить знаходиться всередині слова, чи ні; початково вона не «у слові», маючи значення OUT. Ми надаємо перевагу символічним константам IN і OUTперед буквальними значеннями 1 і 0, оскільки перші роблять програму зрозумілішою. Якщо це маленька програма як от ця, різниця не відчутна, але в більших програмах, покращення прочитності варте цього невеличкого зусилля — написати саме так з самого початку. Ви також дійдете висновку, що набагато легше впроваджувати широкі зміни в програмах, де «магічні» числа з'являються тільки як символічні константи.
Рядочок
nl = nw = nc = 0;
встановлює всі три змінні у значення нуль. Це не є спеціальним випадком — скоріше наслідок того, що присвоєння є виразом із певним значенням, і присвоєння спрягаються з права на ліво. Це так, ніби ми написали б
nl = (nw = (nc = 0));
Оператор || означає АБО, тож рядок
if (c == ' ' || c == '\n' || c = '\t')
можна озвучити як «якщо c є пробілом АБО c є символом нового рядка АБО c є кроком табуляції». (Якщо пригадуєте, екранована послідовність \t є видимим представленням табуляції.) Існує відповідний оператор &&, що означає логічне I (ТА), його пріоритет вищий за ||. Вирази, поєднані && або || оцінюються з ліва на право і гарантовано, що оцінювання припиниться, як тільки істинність чи хибність стане відомою. Якщо c є пробілом, то необхідність перевіряти, чи cдорівнює символу нового рядка, чи табуляції відпадає, тож ці перевірки опущено. Це не настільки важливо тут, але суттєво в складніших випадках, як ми скоро побачимо.
Цей приклад також демонструє else, який описує альтернативну дію, якщо умовна частина твердження if виявиться хибною. Загальною формою буде
if (вираз)
твердження1
else
твердження2
Одне, і тільки одне, з двох тверджень, пов'язаних з if-else, буде виконано. Якщо вираз в дужках є істинним, буде виконано твердження1, якщо ні — твердження2. Твердження можуть бути як одним, так і багатьма, включеними у фігурні дужки. У програмі підрахунку слів, після else знаходиться if з двома твердженнями, включеними у фігурні дужки.
Вправа 1-11. Як би ви перевірили програму підрахунку слів? Які типи вводу ймовірно виявлять помилки, якщо такі є?
Вправа 1-12. Напишіть програму, яка би виводила свій ввід по одному слову на рядок.