summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--ru-ru/c-ru.html.markdown506
1 files changed, 506 insertions, 0 deletions
diff --git a/ru-ru/c-ru.html.markdown b/ru-ru/c-ru.html.markdown
new file mode 100644
index 00000000..80f79ffd
--- /dev/null
+++ b/ru-ru/c-ru.html.markdown
@@ -0,0 +1,506 @@
+---
+language: c
+filename: learnc.c
+contributors:
+ - ["Adam Bard", "http://adambard.com/"]
+ - ["Árpád Goretity", "http://twitter.com/H2CO3_iOS"]
+translators:
+ - ["Evlogy Sutormin", "http://evlogii.com"]
+lang: ru-ru
+---
+
+Что ж, Си всё ещё является лидером среди современных высокопроизводительных языков.
+
+Для большинствоа программистов, Си – это самый низкоуровневый язык на котором они когда-либо писали,
+но этот язык даёт больше, чем просто повышение производительности.
+Держите это руководство в памяти и вы сможете использовать Си максимально эффективно.
+
+```c
+// Однострочный комментарий начинается с // - доступно только после С99.
+
+/*
+Многострочный комментарий выглядит так. Работает начиная с С89.
+*/
+
+// Импорт файлов происходит с помощью **#include**
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+// Файлы <в угловых скобочках> будут подключаться из стандартной библиотеки.
+// Свои файлы необходимо подключать с помощью "двойных кавычек".
+#include "my_header.h"
+
+// Объявление функций должно происходить в .h файлах или вверху .c файла.
+void function_1();
+void function_2();
+
+// Точка входа в программу – это функция main.
+int main() {
+ // для форматированного вывода в консоль используется printf
+ // %d – означает, что будем выводить целое число, \n переводит указатель вывода
+ // на новую строчку
+ printf("%d\n", 0); // => напечатает "0"
+ // Каждый оператор заканчивается точкой с запятой.
+
+ ///////////////////////////////////////
+ // Типы
+ ///////////////////////////////////////
+
+ // int обычно имеет длину 4 байта
+ int x_int = 0;
+
+ // shorts обычно имеет длину 2 байта
+ short x_short = 0;
+
+ // chars гарантированно имеет длину 1 байта
+ char x_char = 0;
+ char y_char = 'y'; // Символьные литералы заключаются в кавычки ''
+
+ // long как правило занимает от 4 до 8 байт
+ // long long занимает как минимум 64 бита
+ long x_long = 0;
+ long long x_long_long = 0;
+
+ // float это 32-битное число с плавающей точкой
+ float x_float = 0.0;
+
+ // double это 64-битное число с плавающей точкой
+ double x_double = 0.0;
+
+ // Целые типы могут быть беззнаковыми.
+ unsigned short ux_short;
+ unsigned int ux_int;
+ unsigned long long ux_long_long;
+
+ // sizeof(T) возвращает размер переменной типа Т в байтах.
+ // sizeof(obj) возвращает размер объекта obj в байтах.
+ printf("%zu\n", sizeof(int)); // => 4 (на большинстве машин int занимает 4 байта)
+
+ // Если аргуметом sizeof будет выражение, то этот аргумент вычисляется
+ // ещё во время компиляции кода (кроме динамических массивов).
+ int a = 1;
+ // size_t это беззнаковый целый тип который использует как минимум 2 байта
+ // для записи размера объекта
+ size_t size = sizeof(a++); // a++ считается во время компиляции
+ printf("sizeof(a++) = %zu, где a = %d\n", size, a);
+ // выведет строку "sizeof(a++) = 4, где 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};
+
+ // Индексация массива происходит также как и в других Си-подобных языках.
+ my_array[0]; // => 0
+
+ // Массивы изменяемы. Это просто память как и другие переменные.
+ my_array[1] = 2;
+ printf("%d\n", my_array[1]); // => 2
+
+ // В C99 (а также опционально в C11), массив может быть объявлен динамически.
+ // Размер массива не обязательно должен быть рассчитан на этапе компиляции.
+ printf("Enter the array size: "); // спрашиваем юзера размер массива
+ char buf[0x100];
+ fgets(buf, sizeof buf, stdin);
+ size_t size = strtoul(buf, NULL, 10); // strtoul парсит строку в беззнаковое целое
+ int var_length_array[size]; // объявление динамического массива
+ printf("sizeof array = %zu\n", sizeof var_length_array);
+
+ // Вывод программы (в зависимости от архитектуры) будет таким:
+ // > Enter the array size: 10
+ // > sizeof array = 40
+
+
+ // Строка – это просто массив символов, оканчивающийся нулевым (NUL (0x00)) байтом
+ // представляемым в строке специальным символом '\0'.
+ // Нам не нужно вставлять нулевой байт в строковой литерал,
+ // компилятор всё сделает за нас.
+ 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 i1 = 1, i2 = 2;
+ float f1 = 1.0, f2 = 2.0;
+
+ // Арифметика обычная
+ i1 + i2; // => 3
+ i2 - i1; // => 1
+ i2 * i1; // => 2
+ i1 / i2; // => 0 (0.5, но обрезается до 0)
+
+ f1 / f2; // => 0.5, плюс-минус погрешность потому что,
+ // цифры с плавающей точкой вычисляются неточно!
+
+ // Модуль
+ 11 % 3; // => 2
+
+ // Операции сравнения вам уже знакомы, но в Си нет булевого типа.
+ // Вместо него используется int. 0 это false, всё остальное это true.
+ // Операции сравнения всегда возвращают 1 или 0.
+ 3 == 2; // => 0 (false)
+ 3 != 2; // => 1 (true)
+ 3 > 2; // => 1
+ 3 < 2; // => 0
+ 2 <= 2; // => 1
+ 2 >= 2; // => 1
+
+ // Си это не Питон – операции сравнения могут быть только парными.
+ int a = 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
+
+ // Битовые операторы
+ ~0x0F; // => 0xF0 (побитовое отрицание)
+ 0x0F & 0xF0; // => 0x00 (побитовое И)
+ 0x0F | 0xF0; // => 0xFF (побитовое ИЛИ)
+ 0x04 ^ 0x0F; // => 0x0B (исключающее ИЛИ (XOR))
+ 0x01 << 1; // => 0x02 (побитовый сдвиг влево (на 1))
+ 0x02 >> 1; // => 0x01 (побитовый сдвиг вправо (на 1))
+
+ // Будьте осторожны при сдвиге беззнакового int, эти операции не определены:
+ // - shifting into the sign bit of a signed integer (int a = 1 << 32)
+ // - сдвиг влево отрицательных чисел (int a = -1 << 2)
+ // - shifting by an offset which is >= the width of the type of the LHS:
+ // int a = 1 << 32; // UB if int is 32 bits wide
+
+ ///////////////////////////////////////
+ // Структуры ветвления
+ ///////////////////////////////////////
+
+ // Условный оператор
+ 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) {
+ printf("%d, ", ii++); // инкрементация происходит после того как
+ // знаечние ii передано ("postincrement")
+ } // => prints "0, 1, 2, 3, 4, 5, 6, 7, 8, 9, "
+
+ printf("\n");
+
+ //Цикл с постусловием
+ int kk = 0;
+ do {
+ printf("%d, ", kk);
+ } while (++kk < 10); // инкрементация происходит перед тем как
+ // передаётся знаечние kk ("preincrement")
+ // => prints "0, 1, 2, 3, 4, 5, 6, 7, 8, 9, "
+
+ printf("\n");
+
+ // Цикл со счётчиком
+ int jj;
+ for (jj=0; jj < 10; jj++) {
+ printf("%d, ", jj);
+ } // => prints "0, 1, 2, 3, 4, 5, 6, 7, 8, 9, "
+
+ printf("\n");
+
+ // Ветвление с множественным выбором
+ switch (some_integral_expression) {
+ case 0: // значения должны быть целыми константами (могут быть выражениями)
+ do_stuff();
+ break; // если не написать break; то управление будет передено следующему блоку
+ case 1:
+ do_something_else();
+ break;
+ default:
+ // если не было совпадения, то выполняется блок default:
+ fputs("ошибка!\n", stderr);
+ exit(-1);
+ break;
+ }
+
+
+ ///////////////////////////////////////
+ // Форматирование вывода
+ ///////////////////////////////////////
+
+ // Каждое выражение в Си имеет тип, но вы можете привести один тип к другому
+ // если хотите (с некоторыми константами).
+
+ int x_hex = 0x01; // Вы можете назначать переменные с помощью шеснадцатеричного кода
+
+ // Приведение типов будет пытаться сохранять цифровые значения
+ printf("%d\n", x_hex); // => Prints 1
+ printf("%d\n", (short) x_hex); // => Prints 1
+ printf("%d\n", (char) x_hex); // => Prints 1
+
+ // Типы могут переполняться без предупреждения
+ printf("%d\n", (unsigned char) 257); // => 1 (Max char = 255 if char is 8 bits long)
+
+ // Для определения максимального значения типов `char`, `signed char` и `unisigned char`,
+ // соответственно используйте CHAR_MAX, SCHAR_MAX и UCHAR_MAX макросы из <limits.h>
+
+ // Целые типы могут быть приведены к вещественным и наоборот
+ printf("%f\n", (float)100); // %f formats a float
+ printf("%lf\n", (double)100); // %lf formats a double
+ printf("%d\n", (char)100.0);
+
+ ///////////////////////////////////////
+ // Указатели
+ ///////////////////////////////////////
+
+ // A pointer is a variable declared to store a memory address. Its declaration will
+ // also tell you the type of data it points to. You can retrieve the memory address
+ // of your variables, then mess with them.
+
+
+ // Указатель – это переменная которая хранит адрес в памяти. При объявлении указателя указывается тип данных переменной на которую он будет ссылаться. Вы можете получить адрес любой переменной, а потом работать с ним.
+
+ // Используйте & для получения адреса переменной
+ int x = 0;
+ printf("%p\n", (void *)&x); // => Напечатает адрес в памяти, где лежит переменная x
+ // (%p выводит указатель на void *)
+
+
+ // Для объявления указателя нужно поставить * перед именем.
+ int *px, not_a_pointer; // px это указатель на int
+ px = &x; // сохранит адрес x в px
+ printf("%p\n", (void *)px); // => Напечатает адрес в памяти, где лежит переменная px
+ printf("%zu, %zu\n", sizeof(px), sizeof(not_a_pointer));
+ // => Напечатает "8, 4" в 64 битной системе
+
+ // Для того, чтобы получить знаечние по адресу, напечатайте * перед именем.
+ // Да, использование * при объявлении указателя и получении значения по адресу,
+ // немного запутано, но вы привыкнете.
+ printf("%d\n", *px); // => Напечаатет 0, значение перемененной x
+
+ // Вы также можете изменять значение, на которое указывает указатель.
+ (*px)++; // Инкрементирует значение на которое указывает px на еденицу
+ printf("%d\n", *px); // => Напечатает 1
+ printf("%d\n", x); // => Напечатает 1
+
+ // массивы хорошо использовать для болшого количества однотипных данных
+ int x_array[20];
+ int xx;
+ for (xx = 0; xx < 20; xx++) {
+ x_array[xx] = 20 - xx;
+ } // Объявление x_array с значениями 20, 19, 18,... 2, 1
+
+ // Инициализация указателя на int с адресом массива.
+ int* x_ptr = x_array;
+ // x_ptr сейчас x_ptr указывает на первый элемент массива (со значением 20).
+ // Это рабоатет, потому что имя массива возвращает указатель на первый элемент.
+ // Например, когда массив передаётся в функцию или назначается указателю, он
+ // невявно преобразуется в указатель.
+ // Исключения: когда массив является аргументом для оператор '&':
+ int arr[10];
+ int (*ptr_to_arr)[10] = &arr; // &arr не является 'int *'!
+ // он является "указатель на массив" (из десяти 'int'ов).
+ // или когда массив это строчный литерал или при объявлении массива символов:
+ char arr[] = "foobarbazquirk";
+ // или когда массив является аргументом `sizeof` или `alignof` операторов:
+ int arr[10];
+ int *ptr = arr; // то же самое что и int *ptr = &arr[0];"
+ printf("%zu %zu\n", sizeof arr, sizeof ptr); // напечатает "40, 4" или "40, 8"
+
+ // Декрементация и инкрементация указателей зависит от их типа
+ // (это называется арифметика указателей)
+ printf("%d\n", *(x_ptr + 1)); // => Напечатает 19
+ printf("%d\n", x_array[1]); // => Напечатает 19
+
+ // Вы также можете динамически выделять несколько боков памяти с помощью
+ // функции 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'ы)
+
+ // Работа с памятью с помощью указателей может давать неожиданные и
+ // непредсказуемые результаты.
+ printf("%d\n", *(my_ptr + 21)); // => Напечатает кто-нибудь-знает-что?
+ // Скорей всего программа вылетит.
+
+ // Когда вы закончили работать с памятью, которую ранее выделили, вам необходимо
+ // освободить её, иначе это может вызвать утечку памяти.
+ 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'; // это выполнится и строка теперь "aoo"
+
+ void function_1()
+} // конец функции main()
+
+///////////////////////////////////////
+// Функции
+///////////////////////////////////////
+
+// Синтаксис объявления функции:
+// <возвращаемый тип> <имя функции>(аргументы)
+
+int add_two_ints(int x1, int x2)
+{
+ return x1 + x2; // Используйте return для возврата значения
+}
+
+/*
+Данные в функицию передаются "по значению", но никто не мешает
+вам передавать в функцию указатели и менять данные по указателям.
+
+Например: инвертировать строку прямо в функции
+*/
+
+// void орзначает, что функция ничего не возвражщает
+void str_reverse(char *str_in)
+{
+ char tmp;
+ int ii = 0;
+ size_t len = strlen(str_in); // `strlen()` является частью стандартной библиотеки
+ for (ii = 0; ii < len / 2; ii++) {
+ tmp = str_in[ii];
+ str_in[ii] = str_in[len - ii - 1]; // ii-тый символ с конца
+ str_in[len - ii - 1] = tmp;
+ }
+}
+
+char c[] = "This is a test.";
+str_reverse(c);
+printf("%s\n", c); // => Выведет ".tset a si sihT"
+
+///////////////////////////////////////
+// Типы и структуры определяемые пользователем
+///////////////////////////////////////
+
+// 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 к структуре, для удобства
+typedef struct rectangle rect;
+
+int area(rect r)
+{
+ return r.width * r.height;
+}
+
+// Если вы имеете большую структуру, можно доступаться к ней "по указателю",
+// чтобы избежать копирования всей структуры.
+int area(const rect *r)
+{
+ return r->width * r->height;
+}
+
+///////////////////////////////////////
+// Указатели на функции
+///////////////////////////////////////
+
+/*
+At runtime, functions are located at known memory addresses. Function pointers are
+much like any other pointer (they just store a memory address), but can be used
+to invoke functions directly, and to pass handlers (or callback functions) around.
+However, definition syntax may be initially confusing.
+
+Example: use str_reverse from a pointer
+*/
+
+
+/*
+Во время исполнения функции находятся по известным адресам в памяти.
+Указатель на функцию может быть использован для непосредственного вызова функции.
+Однако синтаксис может сбивать с толку.
+
+Пример: использование str_reverse по указателю
+*/
+void str_reverse_through_pointer(char *str_in) {
+ // Определение функции через указатель.
+ void (*f)(char *); // Сигнатура должна полность совпадать с целевой функцией.
+ f = &str_reverse; // Присвоить фактический адрес (во время исполнения)
+ // "f = str_reverse;" тоже будет работать.
+ //Имя функции (как и массива) возвращает указатель на начало.
+ (*f)(str_in); // Просто вызываем функцию через указатель.
+ // "f(str_in);" или вот так
+}
+```
+
+## На почитать
+
+Лучше всего найдите копию [K&R, aka "The C Programming Language"](https://en.wikipedia.org/wiki/The_C_Programming_Language)
+Это *книга* написанная создателями Си. Но будьте осторожны, она содержит которые больше не считаются хорошими.
+
+Другой хороший ресурс: [Learn C the hard way](http://c.learncodethehardway.org/book/).
+
+Если у вас появился вопрос, почитайте [compl.lang.c Frequently Asked Questions](http://c-faq.com).
+
+Очень важно использовать правильные отступы и ставить пробелы в нужных местах.
+Читаемый код лучше чем красивый или быстрый код.
+Чтобы научиться писать хороший код, почитайте [Linux kernel coding stlye](https://www.kernel.org/doc/Documentation/CodingStyle).
+
+Также не забывайте, что [Google](http://google.com) и [Яндекс](http://yandex.ru) – ваши хорошие друзья.
+
+[1] http://stackoverflow.com/questions/119123/why-isnt-sizeof-for-a-struct-equal-to-the-sum-of-sizeof-of-each-member