diff options
| author | Anastasiia Bondarenko <nastiasunrise@gmail.com> | 2021-08-22 22:30:29 +0300 | 
|---|---|---|
| committer | GitHub <noreply@github.com> | 2021-08-22 21:30:29 +0200 | 
| commit | ed5f7694fdfd114cbdf9f299ee54ffaf77059cd9 (patch) | |
| tree | a504f5e35275620f16f624d682b13e9ecf9b6853 | |
| parent | 757ed9b49b8de4a3147fe0a44f91960095fdbc55 (diff) | |
[c/uk-ua] Add ukrainian translation for Clang (#3762)
* [c/uk-ua] Add ukrainian translation for Clang
* Apply suggestions from code review
Co-authored-by: Andre Polykanine <ap@oire.me>
| -rw-r--r-- | uk-ua/c-ua.html.markdown | 860 | 
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) | 
