---
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)
