Вступ

У цьому семінарі йде мова про цикли (повторення матеріалу), масиви, таблицю 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

Оператор %

Оператор % використовують для отримання залишку від ділення одного числа на інше. Він має такий самий пріоритет, як оператори множення та ділення:

  1. 55 % 10 = 5
  2. 3 % 5 = 3
  3. 8 % 8 = 0
  4. 16 % 15 = 1
  5. (1 + 2) * 2 % 2 + 2 = 2