summaryrefslogtreecommitdiffhomepage
path: root/uk-ua
diff options
context:
space:
mode:
Diffstat (limited to 'uk-ua')
-rw-r--r--uk-ua/c-ua.html.markdown860
1 files changed, 860 insertions, 0 deletions
diff --git a/uk-ua/c-ua.html.markdown b/uk-ua/c-ua.html.markdown
new file mode 100644
index 00000000..c4dac75f
--- /dev/null
+++ b/uk-ua/c-ua.html.markdown
@@ -0,0 +1,860 @@
+---
+language: c
+filename: learnc-ua.c
+contributors:
+ - ["Adam Bard", "http://adambard.com/"]
+ - ["Árpád Goretity", "http://twitter.com/H2CO3_iOS"]
+ - ["Jakub Trzebiatowski", "http://cbs.stgn.pl"]
+ - ["Marco Scannadinari", "https://marcoms.github.io"]
+ - ["Zachary Ferguson", "https://github.io/zfergus2"]
+ - ["himanshu", "https://github.com/himanshu81494"]
+ - ["Joshua Li", "https://github.com/JoshuaRLi"]
+ - ["Dragos B. Chirila", "https://github.com/dchirila"]
+translators:
+ - ["AstiaSun", "https://github.com/AstiaSun"]
+lang: uk-ua
+---
+
+О, C! Досі мова для сучасних обчислень у високопродуктивних продуктах.
+
+C це імовірно найбільш низькорівнева мова, яку будуть використовувати більшість програмістів. Проте, вона компенсує це не тільки швидкістю виконання. Як тільки ви оціните її можливість ручного управління пам'яттю, С зможе відвести саме в ті місця, в які вам потрібно було потрапити.
+
+> **Дещо про прапори компілятора**
+>
+> За замовчуванням, gcc та clang досить тихо інформують про попередження та помилки
+> при компіляції, хоч це і може бути дуже корисною інформацією. Тому рекомендується
+> використовувати більш вимогливий компілятор. Ось кілька рекомендацій:
+>
+> `-Wall -Wextra -Werror -O2 -std=c99 -pedantic`
+>
+> За інформацією про ці та інші прапори зверніться до головної сторінки man вашого
+> компілятора C (наприклад, `man 1 gcc`) або ж просто заґуґліть.
+
+
+```c
+// Однорядкові коментарі починаються з //
+// Проте вони з'явились тільки після С99.
+
+/*
+Багаторядкові коментарі мають такий вигляд. І працюють в C89.
+*/
+
+/*
+Багаторядкові коментарі не можуть вкладатись один в одний.
+/* Будьте обережними */ // коментар закінчується на цьому рядку...
+*/ // ...а не на цьому!
+
+// Константа: #define <keyword>
+// Назви констант, як правило, пишуться великими літерами, проте це не вимога
+#define DAYS_IN_YEAR 365
+
+// Ще одним способом оголосити константи є перелічення констант.
+// До речі, всі вирази мають закінчуватись крапкою з комою.
+enum days {SUN = 1, MON, TUE, WED, THU, FRI, SAT};
+// MON отримає значення 2 автоматично, TUE дорівнюватиме 3 і т.д.
+
+// Імпортувати заголовки можна за допомогою #include
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+// (Заголовки із стандартної бібліотеки С вказуються між <кутовими дужками>.)
+// Щоб додати власні заголовки, потрібно використовувати "подвійні лапки"
+// замість кутових:
+//#include "my_header.h"
+
+// Сигнатури функцій попередньо оголошуються в .h файлах або на початку .с файлів.
+void function_1();
+int function_2(void);
+
+// Потрібно оголосити 'прототип функції' перед main(), реалізація функцій
+// відбувається після функції main().
+int add_two_ints(int x1, int x2); // прототип функції
+// Варіант `int add_two_ints(int, int);` теж правильний (не потрібно називати
+// аргументи). Рекомендується також називати аргументи в прототипі для
+// кращого розуміння.
+
+// Вхідною точкою програми є функція під назвою main. Вона повертає чисельний тип.
+int main(void) {
+ // реалізація програми
+}
+
+// Аргументи командного рядка, вказані при запуску програми, також передаються
+// у функцію main.
+// argc - це кількість переданих аргументів
+// argv — це масив масивів символів, що містить самі аргументи
+// argv[0] - назва програми, argv[1] - перший аргумент, і т.д.
+int main (int argc, char** argv)
+{
+ // printf дозволяє вивести на екран значення, вивід - це форматований рядок,
+ // в даному випадку %d позначає чисельне значення, \n — це новий рядок
+ printf("%d\n", 0); // => Виводить 0
+
+ ///////////////////////////////////////
+ // Типи
+ ///////////////////////////////////////
+
+ // Всі змінні повинні бути оголошені на початку поточного блоку області видимості.
+ // В цьому коді вони оголошуються динамічно. С99-сумісні компілятори
+ // дозволяють оголошення близько до місця, де значення використовується.
+
+ // int (цілочисельний знаковий тип) зазвичай займає 4 байти
+ int x_int = 0;
+
+ // short (цілочисельний знаковий тип) зазвичай займає 2 байти
+ //
+ short x_short = 0;
+
+ // Символьний тип char гарантовано займає 1 байт
+ char x_char = 0;
+ char y_char = 'y'; // Символьні літерали позначаються ''
+
+ // long (цілочисельний знаковий тип) має розмір від 4 до 8 байтів; великі значення
+ // типу long гарантовано займають 8 байтів
+ long x_long = 0;
+ long long x_long_long = 0;
+
+ // Тип float - це зазвичай 32-бітове число з плаваючою крапкою
+ float x_float = 0.0f; // Суфікс 'f' позначає літерал з плаваючою крапкою
+
+ // Тип double - це зазвийчай 64-бітове число з плаваючою крапкою
+ double x_double = 0.0; // дійсне число без суфіксів має тип double
+
+ // Цілочисельні типи можуть не мати знаку (бути більше, або ж рівними нулю)
+ unsigned short ux_short;
+ unsigned int ux_int;
+ unsigned long long ux_long_long;
+
+ // Char всередині одинарних лапок інтерпретуються як числа в наборі
+ // символів комп'ютера.
+ '0'; // => 48 в таблиці ASCII.
+ 'A'; // => 65 в таблиці ASCII.
+
+ // sizeof(T) повертає розмір змінної типу Т в байтах
+ // sizeof(obj) віддає розмір виразу (змінна, літерал, і т.п.)
+ printf("%zu\n", sizeof(int)); // => 4 (на більшості пристроїв з 4-байтним словом)
+
+ // Якщо аргумент оператора `sizeof` — це вираз, тоді його аргументи не оцінюються
+ // (крім масивів, розмір яких залежить від змінної).
+ // Значення, що повертається в цьому випадку, - це константа часу компіляції.
+ int a = 1;
+ // size_t - беззнаковий чисельний тип розміром щонайменше 2 байти, який
+ // використовується для відображення розміру об'єкта.
+ size_t size = sizeof(a++); // a++ не оцінюється
+ printf("sizeof(a++) = %zu where a = %d\n", size, a);
+ // Виводить "sizeof(a++) = 4 where a = 1" (на 32-бітній архітектурі)
+
+ // Масиви повинні бути проініціалізовані з конкретним розміром.
+ char my_char_array[20]; // Цей масив займає 1 * 20 = 20 байтів
+ int my_int_array[20]; // Цей масив займає 4 * 20 = 80 байтів
+ // (припускаючи 4-байтні числа)
+
+ // Таким чином можна проініціалізувати масив нулем:
+ char my_array[20] = {0};
+ // де "{0}" називається "ініціалізатором масиву".
+
+ // Зазначте, можна явно не оголошувати розмір масиву, ЯКЩО ви проініціалізуєте
+ // масив у тому ж рядку. Тому, наступне оголошення еквівалентне:
+ char my_array[] = {0};
+ // АЛЕ, потрібно визначити розмір масиву під час виконання, як тут:
+ size_t my_array_size = sizeof(my_array) / sizeof(my_array[0]);
+
+ // ПОПЕРЕДЖЕННЯ якщо ви вирішили використовувати даний підхід, потрібно
+ // визначити розмір **перед тим**, як ви почнете передавати масив у функцію
+ // (побачите дискусію пізніше). Масиви перетворюються на вказівники при
+ // передачі як аргументи у функцію, тому попереднє твердження буде видавати
+ // хибний результат всередині функції.
+
+ // Індексація по масиву така ж сама, як і в інших мовах програмування або,
+ // скоріше, як у інших с-подібних мовах.
+ my_array[0]; // => 0
+
+ // Масиви незмінні, це просто частина пам'яті!
+ my_array[1] = 2;
+ printf("%d\n", my_array[1]); // => 2
+
+ // Масиви, розмір яких залежить від змінної, в С99 (та в С11 як вибірковий
+ // функціонал) можуть бути оголошені також. Розмір такого масиву не має бути
+ // константою під час компіляції:
+ printf("Enter the array size: "); // спитати користувача розмір масиву
+ int array_size;
+ fscanf(stdin, "%d", &array_size);
+ int var_length_array[array_size]; // оголосити масив
+ printf("sizeof array = %zu\n", sizeof var_length_array);
+
+ // Приклад:
+ // > Enter the array size: 10
+ // > sizeof array = 40
+
+ // Рядки - це просто масиви символьних літералів (char), що закінчуються NULL
+ // (0x00) байтом, представленим у рядках як спеціальний символ '\0'.
+ // (Не потрібно включати байт NULL в рядкові літерали; компілятор сам вставляє
+ // його наприкінці масиву.)
+ char a_string[20] = "This is a string";
+ printf("%s\n", a_string); // %s форматує рядок
+
+ printf("%d\n", a_string[16]); // => 0
+ // тобто, байт #17 - це 0 (так само, як і 18-ий, 19-ий, та 20-ий)
+
+ // Якщо між одинарними лапками є букви, тоді це символьний літерал.
+ // Він має тип `int`, а не `char` (так історично склалось).
+ int cha = 'a'; // добре
+ char chb = 'a'; // також добре (неявне перетворення з int на char)
+
+ // Багатовимірні масиви:
+ int multi_array[2][5] = {
+ {1, 2, 3, 4, 5},
+ {6, 7, 8, 9, 0}
+ };
+ // Доступ до елементів:
+ int array_int = multi_array[0][2]; // => 3
+
+ ///////////////////////////////////////
+ // Оператори
+ ///////////////////////////////////////
+
+ // Скорочення для багатьох оголошень:
+ int i1 = 1, i2 = 2;
+ float f1 = 1.0, f2 = 2.0;
+
+ int b, c;
+ b = c = 0;
+
+ // Арифметичні операції
+ i1 + i2; // => 3
+ i2 - i1; // => 1
+ i2 * i1; // => 2
+ i1 / i2; // => 0 (0.5 округлено до 0)
+
+ // Потрібно перетворити хоча б одну з цілочисельних змінних на float, щоб
+ // отримати результат з плаваючою крапкою
+ (float)i1 / i2; // => 0.5f
+ i1 / (double)i2; // => 0.5 // Так само і для типу double
+ f1 / f2; // => 0.5, з певною точністю
+ // Такі обчислення не є точними
+
+ // Ділення за модулем також є
+ 11 % 3; // => 2, остача від ділення
+
+ // Оператори порівняння ймовірно схожі, проте в С немає логічного типу.
+ // Натомість використовується int.
+ // (Або _Bool або bool в C99.)
+ // 0 - хибно (false), всі інші значення - правда (true). Оператори
+ // порівняння завжди повертають 0 або 1.
+ 3 == 2; // => 0 (false)
+ 3 != 2; // => 1 (true)
+ 3 > 2; // => 1
+ 3 < 2; // => 0
+ 2 <= 2; // => 1
+ 2 >= 2; // => 1
+
+ // C - це не Python, порівняння не утворюють ланцюги.
+ // Попередження: Рядок нижче скомпілюється, але він означає `(0 < a) < 2`.
+ // В даному випадку, це 1, тому що (0 < 1).
+ int between_0_and_2 = 0 < a < 2;
+ // Натомість потрібно використати:
+ int between_0_and_2 = 0 < a && a < 2;
+
+ // Логічні оператори з числами
+ !3; // => 0 (Логічне НЕ)
+ !0; // => 1
+ 1 && 1; // => 1 (Логічне І)
+ 0 && 1; // => 0
+ 0 || 1; // => 1 (Логічне АБО)
+ 0 || 0; // => 0
+
+ // Тернарний вираз з умовою ( ? : )
+ int e = 5;
+ int f = 10;
+ int z;
+ z = (e > f) ? e : f; // => 10 "if e > f return e, else return f."
+
+ // Оператори збільшення та зменшення на 1:
+ int j = 0;
+ int s = j++; // Повернути j ПОТІМ збільшити j. (s = 0, j = 1)
+ s = ++j; // Збільшити j ПОТІМ повернути j. (s = 2, j = 2)
+ // так само і для j-- та --j
+
+ // Побітові операції!
+ ~0x0F; // => 0xFFFFFFF0 (побітове заперечення, "перше доповнення", результат
+ // для 32-бітного int)
+ 0x0F & 0xF0; // => 0x00 (побітове І)
+ 0x0F | 0xF0; // => 0xFF (побітове АБО)
+ 0x04 ^ 0x0F; // => 0x0B (побітове XOR)
+ 0x01 << 1; // => 0x02 (побітовий зсув вліво (на 1))
+ 0x02 >> 1; // => 0x01 (побітовий зсув вправо (на 1))
+
+ // Будьте обережними при зсуві цілочисельних значень зі знаком.
+ // Наступні дії дають невизначений результат:
+ // - зсув на біт, що зберігає знак числа (int a = 1 << 31)
+ // - зсув вліво на від'ємне число (int a = -1 << 2)
+ // - зсув на число, що більше за ширину типу
+ // TODO: LHS
+ // - зсув на зміщення, що >= ширині типу в лівій частині виразу:
+ // int a = 1 << 32; // Невизначена поведінка, якщо ширина int 32 біти.
+
+ ///////////////////////////////////////
+ // Структури розгалуження
+ ///////////////////////////////////////
+
+ // Оператор умови
+ if (0) {
+ printf("I am never run\n"); // ніколи не буде виконано
+ } else if (0) {
+ printf("I am also never run\n"); // теж ніколи не буде виконано
+ } else {
+ printf("I print\n"); // це буде надруковано
+ }
+
+ // Цикл з передумовою
+ int ii = 0;
+ while (ii < 10) { // БУДЬ-ЯКЕ значення, що менше 10 - правда.
+ printf("%d, ", ii++); // ii++ збільшує ii на 1 ПІСЛЯ передачі поточного значення.
+ } // => надрукує "0, 1, 2, 3, 4, 5, 6, 7, 8, 9, "
+
+ printf("\n");
+
+ // Цикл з післяумовою
+ int kk = 0;
+ do {
+ printf("%d, ", kk);
+ } while (++kk < 10); // ++kk збільшує kk на 1 ПЕРЕД передачою поточного значення.
+ // => надрукує "0, 1, 2, 3, 4, 5, 6, 7, 8, 9, "
+
+ printf("\n");
+
+ // Цикл з лічильником
+ int jj;
+ for (jj=0; jj < 10; jj++) {
+ printf("%d, ", jj);
+ } // => виводить "0, 1, 2, 3, 4, 5, 6, 7, 8, 9, "
+
+ printf("\n");
+
+ // *****Додатково*****:
+ // Цикли та функції обов'язково повинні мати тіло. Якщо тіло не потрібно:
+ int i;
+ for (i = 0; i <= 5; i++) {
+ ; // використовуйте крапку з комою, щоб симулювати тіло (пусте твердження)
+ }
+ // Або
+ for (i = 0; i <= 5; i++);
+
+ // Розгалуження з множинним вибором: switch()
+ switch (a) {
+ case 0: // значення повинні бути *константними* виразами і мати вбудований тип
+ //(наприклад, перелічення)
+ printf("Hey, 'a' equals 0!\n");
+ break; // якщо не використати break, то управління буде передано наступному блоку
+ case 1:
+ printf("Huh, 'a' equals 1!\n");
+ break;
+ // Будьте обережними, виконання продовжиться до тих пір, поки
+ // не зустрінеться наступний "break".
+ case 3:
+ case 4:
+ printf("Look at that.. 'a' is either 3, or 4\n");
+ break;
+ default:
+ // якщо вираз a не співпадає з описаними значеннями, то виконується
+ // блок default
+ fputs("Error!\n", stderr);
+ exit(-1);
+ break;
+ }
+ /*
+ Використання "goto" в С
+ */
+ typedef enum { false, true } bool;
+ // вводимо таке перелічення, оскільки С не має логічного типу до С99
+ bool disaster = false;
+ int i, j;
+ for(i=0;i<100;++i)
+ for(j=0;j<100;++j)
+ {
+ if((i + j) >= 150)
+ disaster = true;
+ if(disaster)
+ goto error;
+ }
+ error :
+ printf("Error occurred at i = %d & j = %d.\n", i, j);
+ /*
+ https://ideone.com/GuPhd6
+ Даний приклад виведе "Error occurred at i = 51 & j = 99."
+ */
+
+ ///////////////////////////////////////
+ // Приведення до типів
+ ///////////////////////////////////////
+
+ // Кожне значенння в С має тип, але можна перевести значення з одного типу в
+ // інший, якщо потрібно (із деякими обмеженнями).
+
+ int x_hex = 0x01; // Змінним можна присвоювати літерали в шістнадцятковій
+ // системі числення
+
+ // Приведення до типу призведе до спроби зберегти чисельне значення
+ printf("%d\n", x_hex); // => Виводить 1
+ printf("%d\n", (short) x_hex); // => Виводить 1
+ printf("%d\n", (char) x_hex); // => Виводить 1
+
+ // В данному випадку попередження не виникатиме, якщо значення виходить за межі
+ // значення типу
+ printf("%d\n", (unsigned char) 257); // => 1 (максимальне значення char = 255,
+ // якщо char має довжину 8 біт)
+
+ // Для того, щоб дізнатись максимальний розмір `char`, `signed char` або ж
+ // `unsigned char`, потрібно використати макроси CHAR_MAX, SCHAR_MAX та UCHAR_MAX
+ // відповідно з <limits.h>.
+
+ // Вбудовані типи можуть бути приведені до типу із плаваючою крапкою і навпаки.
+ printf("%f\n", (double) 100); // %f завжди перетворює число на double...
+ printf("%f\n", (float) 100); // ...навіть, якщо це float.
+ printf("%d\n", (char)100.0);
+
+ ///////////////////////////////////////
+ // Вказівники
+ ///////////////////////////////////////
+
+ // Вказівник - це змінна, що зберігає адресу у пам'яті. Оголошення вказівника
+ // також потребує інформації про тип об'єкта, на який він вказує. Можна
+ // отримати адресу пам'яті будь-якої змінної, а потім працювати з нею.
+
+ int x = 0;
+ printf("%p\n", (void *)&x); // Оператор & повертає адресу змінної у пам'яті
+ // (%p форматує об'єкт вказівника типу void *)
+ // => Виводить деяку адресу в пам'яті
+
+ // Для оголошення вказівника потрібно поставити * перед його назвою.
+ int *px, not_a_pointer; // px - це вказівник на цілочисельне значення (int)
+ px = &x; // Зберігає адресу змінної x в px
+ printf("%p\n", (void *)px); // => Виводить адресу в пам'яті
+ printf("%zu, %zu\n", sizeof(px), sizeof(not_a_pointer));
+ // => Виводить "8, 4" на звичайній 64-бітній системі
+
+ // Щоб прочитати значення, яке зберігається за адресою, на яку вказує вказівник,
+ // потрібно поставити знак * перед назвою змінної.
+ // Так, * використовується одночасно і для оголошення вказівника, і для отримання
+ // значення за адресою. Звучить заплутано, проте тільки спочатку.
+ printf("%d\n", *px); // => Виводить 0, значення x
+
+ // Можна також змінити значення, на яке посилається вказівник.
+ // Тут звернення до адреси обернене у круглі дужки, тому що
+ // ++ має вищий пріоритет виконання, ніж *.
+ (*px)++; // Збільшити значення, на яке вказує px, на 1
+ printf("%d\n", *px); // => Виводить 1
+ printf("%d\n", x); // => Виводить 1
+
+ // Масиви зручно використовувати для виділення неперервного блоку пам'яті.
+ int x_array[20]; // оголошує масив з 20 елементів (розмір можна задати лише один раз)
+ int xx;
+ for (xx = 0; xx < 20; xx++) {
+ x_array[xx] = 20 - xx;
+ } // Ініціалізує x_array значеннями 20, 19, 18,... 2, 1
+
+ // Оголосити вказівник типу int, який посилається на масив x_array
+ int* x_ptr = x_array;
+ // x_ptr тепер вказує на перший елемент масиву (число 20).
+ //
+ // Це працює, тому що при зверненні до імені масиву повертається вказівник
+ // на перший елемент. Наприклад, коли масив передається у функцію або присвоюється
+ // вказівнику, він неявно приводиться до вказівника.
+ // Виключення:
+ // - коли вказівник передається як аргумент із оператором `&`:
+ int arr[10];
+ int (*ptr_to_arr)[10] = &arr; // &arr НЕ має тип `int *`!
+ // Він має тип "вказівник на масив" (з 10 чисел).
+ // - коли масив - це рядковий літерал, що використовується для ініціалізації
+ // масив символів:
+ char otherarr[] = "foobarbazquirk";
+ // - коли масив - це аргумент операторів `sizeof` або `alignof`:
+ int arraythethird[10];
+ int *ptr = arraythethird; // те ж саме, що з int *ptr = &arr[0];
+ printf("%zu, %zu\n", sizeof(arraythethird), sizeof(ptr));
+ // Ймовірно, виводить "40, 4" або "40, 8"
+
+ // Інкрементація та декрементація вказівника залежить від його типу.
+ // (так звана арифметика вказівників)
+ printf("%d\n", *(x_ptr + 1)); // => Виводить 19
+ printf("%d\n", x_array[1]); // => Виводить 19
+
+ // Можна також динамічно виділити послідовні блоки в пам'яті за допомогою
+ // функції malloc зі стандартної бібліотеки. malloc приймає один аргумент типу
+ // size_t, що описує кількість байтів для виділення (зазвичай із купи, проте це
+ // може бути неправдою на вбудованих системах - стандарт С нічого про це не повідомляє).
+ int *my_ptr = malloc(sizeof(*my_ptr) * 20);
+ for (xx = 0; xx < 20; xx++) {
+ *(my_ptr + xx) = 20 - xx; // my_ptr[xx] = 20-xx
+ } // Проініціалізувати пам'ять значеннями 20, 19, 18, 17... 2, 1 (як int)
+
+ // Будьте обережними із передачею значень, що надаються користувачем, в malloc!
+ // Про всяк випадок, використовуйте calloc в таких ситуаціях (який, на відміну від
+ // malloc, також заповнює пам'ять нулями).
+ int* my_other_ptr = calloc(20, sizeof(int));
+
+ // Немає стандартного способу визначити розмір динамічно виділеного масиву в С.
+ // Через це, якщо масиви будуть часто передаватись в програмі, потрібна інша змінна,
+ // яка буде відслідковувати кількість елементів в масиві. Детальніше в розділі
+ // про функції.
+ size_t size = 10;
+ int *my_arr = calloc(size, sizeof(int));
+ // Додати елемент до масиву.
+ size++;
+ my_arr = realloc(my_arr, sizeof(int) * size);
+ if (my_arr == NULL) {
+ // Не забувайте перевіряти результат виконання realloc на помилки!
+ return
+ }
+ my_arr[10] = 5;
+
+ // Робота з вказівниками може призводити до неочікуваних і непрогнозованих
+ // результатів, якщо звернутись до пам'яті, що не була виділена вами.
+ printf("%d\n", *(my_ptr + 21)); // => Хто зна, що буде виведено.
+ // Може навіть вилетіти з помилкою.
+
+ // Після закінчення роботи із виділеною за допомогою malloc пам'яттю, її обов'язково
+ // потрібно звільнити. Інакше ніхто не зможе нею скористатися, аж поки програма не
+ // завершить свою роботу (така ситуація називається "витоком пам'яті").
+ free(my_ptr);
+
+ // Рядки - це масиви символів, проте вони найчастіше представлені як
+ // вказівник на символ (тобто, вказівник на перший елемент масиву). Вважається
+ // хорошим підходом використовувати `const char *', посилаючись на об'єкт
+ // рядка, оскільки його не можна змінити ("foo"[0] = 'a' ЗАБОРОНЕНО).
+ const char *my_str = "This is my very own string literal";
+ printf("%c\n", *my_str); // => 'T'
+
+ // Це не працюватиме, якщо рядок - це масив (потенційно створений за допомогою
+ // рядкового літерала), що зберігається у частині пам'яті, яку можна перезаписувати:
+ char foo[] = "foo";
+ foo[0] = 'a'; // Дозволяється, foo тепер містить "aoo"
+
+ function_1();
+} // Кінець функції main
+
+///////////////////////////////////////
+// Функції
+///////////////////////////////////////
+
+// Синтаксис оголошення функції:
+// <тип повернення> <назва функції>(<аргументи>)
+
+int add_two_ints(int x1, int x2)
+{
+ return x1 + x2; // Використовуйте return, щоб повернути значення
+}
+
+/*
+Дані у функцію передають за значенням. Коли функція викликається, аргументи, що
+передаються у функцію, копіюються з оригіналів (окрім масивів). Всі зміни над
+значенням аргументів всередині функції не впливають на значення оригіналів.
+
+Використовуйте вказівники, якщо потрібно редагувати безпосередньо оригінальні
+значення аргументів.
+
+Приклад: замінити рядок на обернений.
+*/
+
+// void означає, що функція нічого не повертає
+void str_reverse(char *str_in)
+{
+ char tmp;
+ size_t ii = 0;
+ size_t len = strlen(str_in); // `strlen()` це частина стандартної бібліотеки С
+ // Зауважте: довжина, яку повертає `strlen`, не включає
+ // термінальний NULL байт ('\0')
+ for (ii = 0; ii < len / 2; ii++) { // в C99 можна напряму оголошувати тип `ii` в циклі
+ tmp = str_in[ii];
+ str_in[ii] = str_in[len - ii - 1]; // ii-й символ з кінця
+ str_in[len - ii - 1] = tmp;
+ }
+}
+// Зауважте: для використання strlen() потрібно завантажити файл заголовку string.h
+
+/*
+char c[] = "This is a test.";
+str_reverse(c);
+printf("%s\n", c); // => ".tset a si sihT"
+*/
+/*
+Оскільки можна повертати тільки одну змінну, для зміни значення більшої
+кількості змінних можна використовувати виклик за посиланням
+*/
+void swapTwoNumbers(int *a, int *b)
+{
+ int temp = *a;
+ *a = *b;
+ *b = temp;
+}
+/*
+int first = 10;
+int second = 20;
+printf("first: %d\nsecond: %d\n", first, second);
+swapTwoNumbers(&first, &second);
+printf("first: %d\nsecond: %d\n", first, second);
+// змінні обмінюються значеннями
+*/
+
+/*
+Масиви завжди передаються у функції як вказівники, не зважаючи на тип масиву
+(статичний чи динамічний). Тому всередині функція не знає про розмір масиву.
+*/
+// Розмір масиву завжди має передаватись разом із масивом!
+void printIntArray(int *arr, size_t size) {
+ int i;
+ for (i = 0; i < size; i++) {
+ printf("arr[%d] is: %d\n", i, arr[i]);
+ }
+}
+/*
+int my_arr[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
+int size = 10;
+printIntArray(my_arr, size);
+// виведе "arr[0] is: 1" і т.д.
+*/
+
+// Ключове слово extern використовується, якщо всередині функції потрібно звернутись
+// до змінної, що була оголошена поза функцією.
+int i = 0;
+void testFunc() {
+ extern int i; // використовуємо зовнішню змінну i
+}
+
+// Зробити зовнішню змінну приватною у вихідному файлі за допомогою static:
+static int j = 0; // інші файли, що використовують testFunc2(),
+ // не матимуть доступу до змінної j
+void testFunc2() {
+ extern int j;
+}
+// Ключове слово static робить змінну недоступною для коду поза даною одиницею
+// компіляції. (На більшості систем, одиниця компіляції - це файл).
+// static можна використовувати до глобальних змінних, функцій, локальних
+// змінних у функціях. Локальні змінні, проініціалізовані static, поводять
+// себе як глобальні змінні, проте тільки в межах даного файлу. Статичні
+// змінні ініціалізуються 0, якщо інше значення не було вказане.
+// **Як варіант, функції можна зробити приватними оголосивши їх як static**
+
+///////////////////////////////////////
+// Користувацькі типи та структури
+///////////////////////////////////////
+
+// Ключове слово typedef використовується, щоб створити псевдонім типу
+typedef int my_type;
+my_type my_type_var = 0;
+
+// Структури - це такі собі колекції з даними. Пам'ять для полів виділяється
+// послідовно, в порядку їх написання:
+struct rectangle {
+ int width;
+ int height;
+};
+
+// Проте це не означає, що
+// sizeof(struct rectangle) == sizeof(int) + sizeof(int)
+// в зв'язку з вирівнюванням пам'яті [1]
+
+void function_1()
+{
+ struct rectangle my_rec;
+
+ // Доступ до полів структури відбувається через .
+ my_rec.width = 10;
+ my_rec.height = 20;
+
+ // Можна створити вказівники на структуру
+ struct rectangle *my_rec_ptr = &my_rec;
+
+ // Звернення до структури через вказівник та зміна значень поля:
+ (*my_rec_ptr).width = 30;
+
+ // Але є й альтернативний спосіб звернутись до поля через вказівник, використовуючи
+ // оператор -> (краще читається)
+ my_rec_ptr->height = 10; // Те ж саме, що (*my_rec_ptr).height = 10;
+}
+
+// Можна використати typedef перед struct
+typedef struct rectangle rect;
+
+int area(rect r)
+{
+ return r.width * r.height;
+}
+
+// Якщо ваша структура доволі громіздка, можна звертатись до неї через вказівник,
+// щоб уникнути копіювання всієї структури:
+int areaptr(const rect *r)
+{
+ return r->width * r->height;
+}
+
+///////////////////////////////////////
+// Вказівники на функції
+///////////////////////////////////////
+/*
+Під час виконання функції знаходяться за відомими адресами в пам'яті. Вказівники
+на функції - це ті ж самі вказівники, що зберігають адресу у пам'яті, проте можуть
+використовуватись, щоб викликати функції напряму і передавати обробники (або функції зі
+зворотнім зв'язком). Хоча, синтаксис спочатку може бути доволі незрозумілим.
+
+Приклад: use str_reverse from a pointer
+*/
+void str_reverse_through_pointer(char *str_in) {
+ // Оголосити вказівник на функцію під назвою f.
+ void (*f)(char *); // Сигнатура повинна точно співпадати із цільовою функцією.
+ f = &str_reverse; // Присвойте адресу певної функції (визначається під час виконання)
+ // f = str_reverse; повинно працювати також
+ (*f)(str_in); // Виклик функції через вказівник
+ // f(str_in); // Це альтернативний, але теж вірний синтаксис виклику функції.
+}
+
+/*
+Якщо сигнатури функцій співпадають, можна присвоїти будь-яку функцію тому ж
+самому вказівнику. Вказівники на функції зазвичай використовуються як псевдоніми
+для спрощення та покращення читабельності коду. Приклад:
+*/
+
+typedef void (*my_fnp_type)(char *);
+
+// Використання при оголошенні змінної вказівника:
+// ...
+// my_fnp_type f;
+
+
+// Спеціальні символи:
+/*
+'\a'; // символ попередження (дзвінок)
+'\n'; // символ нового рядка
+'\t'; // символ табуляції (вирівнювання по лівому краю)
+'\v'; // вертикальна табуляція
+'\f'; // нова сторінка
+'\r'; // повернення каретки
+'\b'; // стирання останнього символу
+'\0'; // нульовий символ. Зазвичай розташовується в кінці рядка.
+// hello\n\0. \0 використовується для позначення кінця рядка.
+'\\'; // зворотній слеш
+'\?'; // знак питання
+'\''; // одинарні лапки
+'\"'; // подвійні лапки
+'\xhh'; // шістнадцяткове число. Наприклад: '\xb' = символ вертикальної табуляції
+'\0oo'; // вісімкове число. Наприклад: '\013' = символ вертикальної табуляції
+
+// форматування виводу:
+"%d"; // ціле число (int)
+"%3d"; // ціле число, щонайменше 3 символи (вирівнювання по правому краю)
+"%s"; // рядок
+"%f"; // число з плаваючою крапкою (float)
+"%ld"; // велике ціле число (long)
+"%3.2f"; // число з плаваючою крапкою, щонайменше 3 цифри зліва і 2 цифри справа
+"%7.4s"; // (аналогічно для рядків)
+"%c"; // символ
+"%p"; // вказівник. Зазначте: потребує перетворення типу на (void *) перед
+ // використанням у `printf`.
+"%x"; // шістнадцяткове число
+"%o"; // вісімкове число
+"%%"; // друкує %
+*/
+
+///////////////////////////////////////
+// Порядок виконання
+///////////////////////////////////////
+
+//---------------------------------------------------//
+// Оператори | Асоціативність//
+//---------------------------------------------------//
+// () [] -> . | зліва направо //
+// ! ~ ++ -- + = *(type)sizeof | справа наліво //
+// * / % | зліва направо //
+// + - | зліва направо //
+// << >> | зліва направо //
+// < <= > >= | зліва направо //
+// == != | зліва направо //
+// & | зліва направо //
+// ^ | зліва направо //
+// | | зліва направо //
+// && | зліва направо //
+// || | зліва направо //
+// ?: | справа наліво //
+// = += -= *= /= %= &= ^= |= <<= >>= | справа наліво //
+// , | зліва направо //
+//---------------------------------------------------//
+
+/****************************** Файли заголовків *********************************
+
+Файли заголовків важливі в С. Вони розділяють вихідний код та визначення на різні
+файли, що робить їх кращими для розуміння.
+
+Файли заголовків синтаксично подібні до вихідних файлів С, проте описуються у".h"
+файлах. Їх можна додати в код за допомогою директиви #include "example.h", якщо
+example.h існує в тому ж каталозі, що і файл С.
+*/
+
+/*
+Так можна запобігти тому, що заголовок буде оголошений кілька разів. Така ситуація
+виникає у випадку циклічної залежності, тобто коли вміст заголовку вже було
+оголошено.
+*/
+#ifndef EXAMPLE_H /* якщо EXAMPLE_H ще не оголошено. */
+#define EXAMPLE_H /* Визначити макрос EXAMPLE_H. */
+
+/*
+Заголовки можна додавати в інші заголовки, таким чином вони разом додаються
+у подальшому.
+*/
+#include <string.h>
+
+/*
+Макроси можуть бути визначені також у заголовку та використовуватись у файлах,
+що містять цей заголовок.
+*/
+#define EXAMPLE_NAME "Dennis Ritchie"
+
+/* Макроси функції також можна визначити. */
+#define ADD(a, b) ((a) + (b))
+/*
+Зверніть увагу на круглі дужки навколо аргументів! Важливо переконатись, що
+a та b не можна проінтерпретувати інакше. Наприклад:
+MUL(x, y) (x * y);
+MUL(1 + 2, 3) -> (1 + 2 * 3), що є помилкою
+*/
+
+/* Struct та typedef можуть використовуватись для узгодженості між файлами. */
+typedef struct Node
+{
+ int val;
+ struct Node *next;
+} Node;
+
+/* Так само і перелічення. */
+enum traffic_light_state {GREEN, YELLOW, RED};
+
+/*
+Прототипи функцій також можна оголосити так, щоб використовувати у кількох
+файлах. Але так робити не варто. Краще оголосити їх у С файлі.
+*/
+Node createLinkedList(int *vals, int len);
+
+/*
+Окрім вище згаданих випадків, всі інші визначення мають описуватись у С файлах.
+*/
+
+#endif /* Кінець директиви передкомпіляції if. */
+
+```
+## Додаткові матеріали
+
+Кращим посібником для вивчення С буде книга авторства Деніса Рітчі (творець С) та Браяна Кернігана,
+[K&R, aka "The C Programming Language"](https://en.wikipedia.org/wiki/The_C_Programming_Language).
+Але обережно з нею, книга старезна і містить неточності (ідеї, що вже вважаються не надто прийнятними).
+
+Ще одним хорошим ресурсом є книга "Learn C The Hard Way" (наявна тільки англійською).
+
+На деякі часті запитання дасть відповідь англомовний ресурс [compl.lang.c Frequently Asked Questions](http://c-faq.com).
+
+Нагадаю, що важливо використовувати правильні інтервали, відступи та загалом мати узгоджений стиль коду.
+Зручний для читання код краще, ніж складний код або зроблений нашвидкоруч. За прикладом можна звернутись до
+[Linux kernel coding style](https://www.kernel.org/doc/Documentation/process/coding-style.rst).
+
+Щодо всього іншого, Ґуґл на допомогу!
+
+[1] [Чому розмір структури не дорівнює сумі розмірів її полів? (англ.)](http://stackoverflow.com/questions/119123/why-isnt-sizeof-for-a-struct-equal-to-the-sum-of-sizeof-of-each-member)