Вступ
У цьому семінарі йде мова про цикли (повторення матеріалу), масиви, таблицю ASCII, функції та аргументи командного рядка.
Цикли
Цикли - це спосіб виконати щось декілька разів, не повторюючи код.
Цикл for
Цикли for зазвичай використовують коли відомо, скільки разів треба виконати якусь інструкцію. Загальний вигляд:
for(ініціалізація; умова; оновлення) { // execute this code }
Ініціалізація - це встановлення початкового значення циклу. Наприклад, int i = 0. Поітм йде перевірка умови. Якщо умова істинна - виконується тіло циклу. Потім знов перевіряється умова, і знову виконується тіло. Окрім тіла, виконується інструкція оновлення ініціалізованої раніше змінної (наприклад, i++)
Тобто кожен прохід циклу оновлюється змінна, перевіряється умова і виконується тіло циклу.
Приклад
Вивести на екран "This is CS50!" десять разів:
for(int i = 0; i < 10; i++) { printf("This is CS50!\n"); }
Змінна i на початку циклу встановлюється в 0, на кожному повторенні вона збільшується на 1, і цикл завершується, коли і перестає бути меншим за 10. Таким чином, інструкція в тілі циклу виконується 10 разів.
Цикл while
Цикли while корисні тоді, коли невідомо скільки разів треба виконати якусь дію. Натомість, є умова яка перевіряється при кожному проході циклу:
while(умова) { //Виконувати цей код }
Таким чином, є умова, яка перевіряється при кожному проході циклу. Якщо вона істинна - виконуємо тіло циклу ще раз, якщо хибна - виходимо з нього.
Приклад
Вивести на екран числа від 1 до 10 в зворотньому порядку:
int count = 10; while (count >= 0) { printf("%i\n", count); count--; }
Цикл do/while
Зазвичай застосовуються тоді, коли необхідно виконати цикл хоча б один раз, - наприклад, при очікуванні вводу від користувача.
do { //Виконати цей код } while (умова);
Цикл буде виконано хоча б один раз, після чого пройде перевірка умови, і, якщо вона істинна, то цикл буде виконано ще раз.
Приклад
Запитує введення числа в користувача, допоки він не введе додатнє число:
int input; do { printf("Enter a positive number: "); input = GetInt(); } while(input < 1)
Масиви
Масиви - це структури даних, що дозволяють зберігати однотипні дані в одному місці. В масиві не може бути даних різних типів, і масиви мають визначену довжину.
Нижче зображено масив з довжиною 3, у якому розміщено три цілих числа (int).
Масиви не можуть змінювати свій розмір, бо це визначені комірки в пам'яті.
Оголошення масиву
Щоб створити масив, необхідно оголосити тип даних, які він зберігатиме, і його розмір. Загальний вигляд оголошення масива:
<тип даних> ім'я[<розмір>];
Щоб записати значення у масив, треба вказати порядковий номер "комірки" (індекс), в яку записуємо значення. Нумерація "комірок" масиву починається з нуля!
ім'я[<комірка>] = <значення>;
Приклад: оголошення і заповнення масиву, який зображено вище:
int temperature[3]; temperature[0] = 65; temperature[1] = 87; temperature[2] = 30;
В першу комірку, за індексом 0 буде записано значення 65, у другу (індекс 1) - значення 87, у третю - значення 30.
Також можна ініціалізувати масив, одразу вказавши у фігурних дужках значення, які він буде зберігати. В такому випадку автоматично буде визначено розмір масиву за кількістю значень, що знаходяться у фігурних дужках:
int temperature[] = {65, 87, 30};
У циклі for початковим значенням змінної зазвичай встановлюють нуль якраз тому, що індексація масивів починається з нуля.
Доступ до елементів масиву
Щоб отримати доступ до елемента масиву, необхідно, як і при записі значення, вказати індекс елемента:
ім'я[<комірка>];
Приклад 1
Вивести усі значення з масиву temperature:
for (int i = 0; i < 3; i++) { printf("%d\n", temperature[i]); }
Приклад 2
Запис значень у масив з клавіатури:
#include <stdio.h> #include <cs50.h> #define CLASS_SIZE 30 int main(void) { //Оголошення масиву int scores_array[CLASS_SIZE]; //Заповнення масиву for(int i = 0; i < CLASS_SIZE; i++) { printf("Enter score for student %d: ", i); scores_array[i] = GetInt(); } }
Зверніть увагу, що CLASS_SIZE має значення 30, і ми оголошуємо масив цілих чисел scores_array розміру 30, а потім у циклі for проходимо по масиву 30 разів, кожного разу запитуючи введення нового значення у масив.
Приклад 3
Знайдіть помилку (підказка: помилка в описі циклу for):
string class[3] = { "Sam", "Jess", "Kim" }; for (int i = 0; i <= 3; i++) { printf("%s\n", class[i]); }
Помилка - у виразі i <= 3, бо таким чином у циклі ми спробуємо отримати доступ до комірки масиву з індексом 3. Але у масиві розміру 3 остання комірка має індекс 2.
Масив розміру n має n-1 індексів.
Багатовимірні масиви
Багатовимірні масиви - це своєрідні "масиви масивів", тобто масиви, у комірках яких зберігаються інші масиви.
Оголошуються вони таким самим чином, як і одновимірні, тільки треба вказати не один розмір масиву, а два. Це буде кількість рядків та стовпчиків у масиві. Оголошення масиву, що на рисунку вище:
char board[3][3]; board[1][1] = 'o'; board[0][0] = 'x'; board[2][0] = 'o'; board[0][2] = 'x';
Приклад
Обчислення довжини рядка:
string s = GetString(); int length = 0; while (s[length] != '\0') length++;
Рядок - це масив символів, що завжди закінчується символом '\0'. Тому, для обчислення довжини рядка ми застосовуємо цикл while і збільшуємо змінну length, переглядаючи кожен символ у рядку, поки не зустрінемо символ кінця рядка.
Ще приклад
Створити масив із числами 1, 2, 3. Вивести його вміст на екран:
int example = {1, 2, 3}; for(int i = 0; i < 3; i++) { printf("%i \n", example[i]; }
Функції
Функція - це "чорний ящик", куди потрапляють вхідні дані, над ними відбуваються маніпуляції і на основі цих даних ми отримуємо результат.
Раніше ви вже використовували функції: GetInt(), GetString(), printf(). Кожна з них отримує певні дані, виконує певні дії і повертає певний результат.
Навіщо потрібні функції:
- Упорядкування коду
- Спрощення коду
- Повторне використання
Код простіше підтримувати, коли він розбитий на функції, кожна з яких робить певну невелику частину роботи.
Вам не потрібно знати, як працює функція - головне, що вона робить і який результат її роботи. Це спрощує розуміння великої програми.
Якщо вам потрібно зробити схожі дії у різних частинах програми - можна написати одну функцію, і використати її багато разів.
Структура функції
Функції мають наступну структуру:
<тип результату> ім'я(список параметрів) { <тіло функції> }
Приклад:
- Тип результату
- Ім'я функції
- Список параметрів
- Тіло функції
На С потрібно завжди вказувати тип результату - тобто якого типу буде те значення, яке має повернути функція. У прикладі вище, функція повертає куб цілого числа - це тип int.
Будь-яка назва для функції. Бажано називати функції так, щоб було зрозуміло, що вони роблять. У прикладі функція має назву cube
Перелік вхідних даних для функції. Функція може не мати параметрів, може мати один або декілька параметрів. Для кожного параметра треба вказати його ім'я та тип. У прикладі функція приймає єдиний параметр - ціле число int input.
Тут описують дії, що виконує функція. Слово return вказує на кінець роботи функції і вказує значення, яке повертає функція як результат роботи (значення змінної output у прикладі). Функція може і не повертати результату.
Прототип функції
Прототипом функції називають оголошення лише заголовку функції, без тіла. Прототип вказує компілятору, що існує функція з певним ім'ям, типом результату та списком параметрів, тіло якої буде описано пізніше у коді.
Приклад
У прикладі нижче у головній функції ми знаходимо куб числа x, використовуючи функцію cube:
#include <stdio.h> int cube(int input); //Прототип функції cube int main(void) { int x = 2; printf("x is %i\n", x); x = cube(x); printf("x is %i\n", x); } int cube(int input) { int output = input * input * input; return output; }
Структура пам'яті програми
Структура стеку (для програми знаходження кубу числа):
Коли ви викликаєте якусь функцію, її паметри та локальні змінні розташовуються у стеку над тією функцією, у якій її викликали (cube() розташовано над main()). Коли функція завершує роботу, вона передає результат функції, що знаходиться під нею у стеку.
Приклад
Програма, що міняє місцями значення змінних x та y
#include <stdio.h> int swap(int a, int b); //Прототип функції swap int main(void) { int x = 1, y = 2; swap(x, y); printf("x is %i\n", x); printf("y is %i\n", y); } void swap(int a, int b) { int tmp = a; a = b; b = tmp; }
Ця програма не працює - змінні x та y у програмі не змінюються. Це відбувається тому, що у функції передаються копії значень змінних.
Агрументи командного рядка
Раніше у програмах ми писали:
int main(void)
Проте, у більшості програм пишуть так:
int main(int argc, string argv[])
Тут int argc - це лічильник кількості аргументів командного рядка,
string argv[] - масив, що містить аргументи командного рядка.
Коли ви запускаєте у командному рядку якусь програму, після імені програми ви можете вказати ще й параметри, які ви хочете їй передати. Ці параметри і є аргументами командного рядка.
У цьому прикладі:
- argc == 3
- argv[0] == ./copy
- argv[1] == infile
- argv[2] == outfile
ASCII
ASCII - це таблиця, в якій символам співставляються числові значення.
Так як можна однозначно переводити символи в числа і навпаки, наступний код буде робочим:
printf("%d\n", 'a' - 'A'); //32 printf("%c\n", 'B' + ('a' - 'A')); //b printf("%c\n", 'b' - ('a' - 'A')); //B printf("%c\n", 'B' + 1); //C printf("%c\n", ('z' - 'a' + 1) % 26 + 'a'); //a
В коментарях до кожного рядку у прикладі вище вказано результат, який виводить команда.
Функція atoi()
Функція atoi() перетворює рядок, що містить числа, на число. За допомогою цієї функції можна зробити так:
string number = "125"; int a = atoi(number); printf("%d\n", a); //125
Оператор %
Оператор % використовують для отримання залишку від ділення одного числа на інше. Він має такий самий пріоритет, як оператори множення та ділення:
- 55 % 10 = 5
- 3 % 5 = 3
- 8 % 8 = 0
- 16 % 15 = 1
- (1 + 2) * 2 % 2 + 2 = 2