[c/ru] some fixes and improves

This commit is contained in:
Andrew Popov 2013-11-17 16:35:17 +04:00
parent ed09da6cdd
commit b548dad5ee

View File

@ -50,10 +50,10 @@ int main() {
// int обычно имеет длину 4 байта // int обычно имеет длину 4 байта
int x_int = 0; int x_int = 0;
// shorts обычно имеет длину 2 байта // short обычно имеет длину 2 байта
short x_short = 0; short x_short = 0;
// chars гарантированно имеет длину 1 байта // char гарантированно имеет длину 1 байта
char x_char = 0; char x_char = 0;
char y_char = 'y'; // Символьные литералы заключаются в кавычки '' char y_char = 'y'; // Символьные литералы заключаются в кавычки ''
@ -82,7 +82,7 @@ int main() {
int a = 1; int a = 1;
// size_t это беззнаковый целый тип который использует как минимум 2 байта // size_t это беззнаковый целый тип который использует как минимум 2 байта
// для записи размера объекта // для записи размера объекта
size_t size = sizeof(a++); // a++ считается во время компиляции size_t size = sizeof(a++); // a++ не выполнится
printf("sizeof(a++) = %zu, где a = %d\n", size, a); printf("sizeof(a++) = %zu, где a = %d\n", size, a);
// выведет строку "sizeof(a++) = 4, где a = 1" (на 32-битной архитектуре) // выведет строку "sizeof(a++) = 4, где a = 1" (на 32-битной архитектуре)
@ -134,7 +134,7 @@ int main() {
// Операторы // Операторы
/////////////////////////////////////// ///////////////////////////////////////
// Можно использовать множественное объявление // Можно использовать множественное объявление.
int i1 = 1, i2 = 2; int i1 = 1, i2 = 2;
float f1 = 1.0, f2 = 2.0; float f1 = 1.0, f2 = 2.0;
@ -184,10 +184,9 @@ int main() {
0x02 >> 1; // => 0x01 (побитовый сдвиг вправо (на 1)) 0x02 >> 1; // => 0x01 (побитовый сдвиг вправо (на 1))
// Будьте осторожны при сдвиге беззнакового int, эти операции не определены: // Будьте осторожны при сдвиге беззнакового int, эти операции не определены:
// - shifting into the sign bit of a signed integer (int a = 1 << 32) // - сдвиг в знаковый бит у целого числа (int a = 1 << 32)
// - сдвиг влево отрицательных чисел (int a = -1 << 2) // - сдвиг влево отрицательных чисел (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
/////////////////////////////////////// ///////////////////////////////////////
// Структуры ветвления // Структуры ветвления
@ -231,7 +230,7 @@ int main() {
// Ветвление с множественным выбором // Ветвление с множественным выбором
switch (some_integral_expression) { switch (some_integral_expression) {
case 0: // значения должны быть целыми константами (могут быть выражениями) case 0: // значения должны быть целыми константами (и могут быть выражениями)
do_stuff(); do_stuff();
break; // если не написать break; то управление будет передено следующему блоку break; // если не написать break; то управление будет передено следующему блоку
case 1: case 1:
@ -249,23 +248,23 @@ int main() {
// Форматирование вывода // Форматирование вывода
/////////////////////////////////////// ///////////////////////////////////////
// Каждое выражение в Си имеет тип, но вы можете привести один тип к другому // Каждое выражение в Си имеет тип, но вы можете привести один тип к другому,
// если хотите (с некоторыми константами). // если хотите (с некоторыми искажениями).
int x_hex = 0x01; // Вы можете назначать переменные с помощью шеснадцатеричного кода int x_hex = 0x01; // Вы можете назначать переменные с помощью шеснадцатеричного кода.
// Приведение типов будет пытаться сохранять цифровые значения // Приведение типов будет пытаться сохранять цифровые значения.
printf("%d\n", x_hex); // => Prints 1 printf("%d\n", x_hex); // => Prints 1
printf("%d\n", (short) x_hex); // => Prints 1 printf("%d\n", (short) x_hex); // => Prints 1
printf("%d\n", (char) 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) printf("%d\n", (unsigned char) 257); // => 1 (Max char = 255 if char is 8 bits long)
// Для определения максимального значения типов `char`, `signed char` и `unisigned char`, // Для определения максимального значения типов `char`, `signed char` и `unisigned char`,
// соответственно используйте CHAR_MAX, SCHAR_MAX и UCHAR_MAX макросы из <limits.h> // соответственно используйте CHAR_MAX, SCHAR_MAX и UCHAR_MAX макросы из <limits.h>
// Целые типы могут быть приведены к вещественным и наоборот // Целые типы могут быть приведены к вещественным и наоборот.
printf("%f\n", (float)100); // %f formats a float printf("%f\n", (float)100); // %f formats a float
printf("%lf\n", (double)100); // %lf formats a double printf("%lf\n", (double)100); // %lf formats a double
printf("%d\n", (char)100.0); printf("%d\n", (char)100.0);
@ -274,14 +273,11 @@ int main() {
// Указатели // Указатели
/////////////////////////////////////// ///////////////////////////////////////
// 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; int x = 0;
printf("%p\n", (void *)&x); // => Напечатает адрес в памяти, где лежит переменная x printf("%p\n", (void *)&x); // => Напечатает адрес в памяти, где лежит переменная x
// (%p выводит указатель на void *) // (%p выводит указатель на void *)
@ -295,37 +291,37 @@ int main() {
// => Напечатает "8, 4" в 64 битной системе // => Напечатает "8, 4" в 64 битной системе
// Для того, чтобы получить знаечние по адресу, напечатайте * перед именем. // Для того, чтобы получить знаечние по адресу, напечатайте * перед именем.
// Да, использование * при объявлении указателя и получении значения по адресу, // Да, использование * при объявлении указателя и получении значения по адресу
// немного запутано, но вы привыкнете. // немного запутано, но вы привыкнете.
printf("%d\n", *px); // => Напечаатет 0, значение перемененной x printf("%d\n", *px); // => Напечаатет 0, значение перемененной x
// Вы также можете изменять значение, на которое указывает указатель. // Вы также можете изменять значение, на которое указывает указатель.
(*px)++; // Инкрементирует значение на которое указывает px на еденицу (*px)++; // Инкрементирует значение на которое указывает px на единицу
printf("%d\n", *px); // => Напечатает 1 printf("%d\n", *px); // => Напечатает 1
printf("%d\n", x); // => Напечатает 1 printf("%d\n", x); // => Напечатает 1
// массивы хорошо использовать для болшого количества однотипных данных // Массивы удобно использовать для болшого количества однотипных данных.
int x_array[20]; int x_array[20];
int xx; int xx;
for (xx = 0; xx < 20; xx++) { for (xx = 0; xx < 20; xx++) {
x_array[xx] = 20 - xx; x_array[xx] = 20 - xx;
} // Объявление x_array с значениями 20, 19, 18,... 2, 1 } // Объявление x_array с значениями 20, 19, 18,... 2, 1
// Инициализация указателя на int с адресом массива. // Объявление указателя на int с адресом массива.
int* x_ptr = x_array; int* x_ptr = x_array;
// x_ptr сейчас x_ptr указывает на первый элемент массива (со значением 20). // x_ptr сейчас указывает на первый элемент массива (со значением 20).
// Это рабоатет, потому что имя массива возвращает указатель на первый элемент. // Это рабоатет, потому что имя массива возвращает указатель на первый элемент.
// Например, когда массив передаётся в функцию или назначается указателю, он // Например, когда массив передаётся в функцию или назначается указателю, он
// невявно преобразуется в указатель. // невявно преобразуется в указатель.
// Исключения: когда массив является аргументом для оператор '&': // Исключения: когда массив является аргументом для оператор '&':
int arr[10]; int arr[10];
int (*ptr_to_arr)[10] = &arr; // &arr не является 'int *'! int (*ptr_to_arr)[10] = &arr; // &arr не является 'int *'!
// он является "указатель на массив" (из десяти 'int'ов). // он является "указателем на массив" (из десяти 'int'ов).
// или когда массив это строчный литерал или при объявлении массива символов: // или когда массив это строчный литерал, используемый при объявлении массива символов:
char arr[] = "foobarbazquirk"; char arr[] = "foobarbazquirk";
// или когда массив является аргументом `sizeof` или `alignof` операторов: // или когда массив является аргументом `sizeof` или `alignof` операторов:
int arr[10]; int arr[10];
int *ptr = arr; // то же самое что и int *ptr = &arr[0];" int *ptr = arr; // то же самое что и "int *ptr = &arr[0];"
printf("%zu %zu\n", sizeof arr, sizeof ptr); // напечатает "40, 4" или "40, 8" printf("%zu %zu\n", sizeof arr, sizeof ptr); // напечатает "40, 4" или "40, 8"
// Декрементация и инкрементация указателей зависит от их типа // Декрементация и инкрементация указателей зависит от их типа
@ -333,7 +329,7 @@ int main() {
printf("%d\n", *(x_ptr + 1)); // => Напечатает 19 printf("%d\n", *(x_ptr + 1)); // => Напечатает 19
printf("%d\n", x_array[1]); // => Напечатает 19 printf("%d\n", x_array[1]); // => Напечатает 19
// Вы также можете динамически выделять несколько боков памяти с помощью // Вы также можете динамически выделять несколько блоков памяти с помощью
// функции malloc из стандартной библиотеки, которая принимает один // функции malloc из стандартной библиотеки, которая принимает один
// аргумент типа size_t – количество байт необходимых для выделения. // аргумент типа size_t – количество байт необходимых для выделения.
int *my_ptr = malloc(sizeof(*my_ptr) * 20); int *my_ptr = malloc(sizeof(*my_ptr) * 20);
@ -347,13 +343,13 @@ int main() {
// Скорей всего программа вылетит. // Скорей всего программа вылетит.
// Когда вы закончили работать с памятью, которую ранее выделили, вам необходимо // Когда вы закончили работать с памятью, которую ранее выделили, вам необходимо
// освободить её, иначе это может вызвать утечку памяти. // освободить её, иначе это может вызвать утечку памяти или ошибки.
free(my_ptr); free(my_ptr);
// Строки это массивы символов, но обычно они представляются как // Строки это массивы символов, но обычно они представляются как
// указатели на символ (как указатели на первый элемент массива). // указатели на символ (как указатели на первый элемент массива).
// Хорошей практикой считается использование `const char *' при объявлении // Хорошей практикой считается использование `const char *' при объявлении
// строчоного литерала. При таком подходе литерал не может быть изменён. // строчного литерала. При таком подходе литерал не может быть изменён.
// (например "foo"[0] = 'a' вызовет ошибку!) // (например "foo"[0] = 'a' вызовет ошибку!)
const char *my_str = "This is my very own string literal"; const char *my_str = "This is my very own string literal";
@ -382,13 +378,13 @@ int add_two_ints(int x1, int x2)
} }
/* /*
Данные в функицию передаются "по значению", но никто не мешает Данные в функцию передаются "по значению", но никто не мешает
вам передавать в функцию указатели и менять данные по указателям. вам передавать в функцию указатели и менять данные по указателям.
Например: инвертировать строку прямо в функции Например: инвертировать строку прямо в функции
*/ */
// void орзначает, что функция ничего не возвражщает // void означает, что функция ничего не возвращает
void str_reverse(char *str_in) void str_reverse(char *str_in)
{ {
char tmp; char tmp;
@ -409,11 +405,11 @@ printf("%s\n", c); // => Выведет ".tset a si sihT"
// Типы и структуры определяемые пользователем // Типы и структуры определяемые пользователем
/////////////////////////////////////// ///////////////////////////////////////
// typedef исапользуется для задания стандартным типам своих названий // typedef используется для задания стандартным типам своих названий
typedef int my_type; typedef int my_type;
my_type my_type_var = 0; my_type my_type_var = 0;
// Структыры это просто коллекция данных, память выделяется последовательно, // Структуры это просто коллекция данных, память выделяется последовательно,
// в том порядке в котором записаны данные. // в том порядке в котором записаны данные.
struct rectangle { struct rectangle {
int width; int width;
@ -421,7 +417,7 @@ struct rectangle {
}; };
// sizeof(struct rectangle) == sizeof(int) + sizeof(int) не всегда верно // sizeof(struct rectangle) == sizeof(int) + sizeof(int) не всегда верно
// из-за особенностей компиляции (проблема в отступах)[1]. // из-за особенностей компиляции (необычное поведение при отступах)[1].
void function_1() void function_1()
{ {
@ -441,7 +437,7 @@ void function_1()
my_rec_ptr->height = 10; // то же что и "(*my_rec_ptr).height = 10;" my_rec_ptr->height = 10; // то же что и "(*my_rec_ptr).height = 10;"
} }
// Вы можете применить typedef к структуре, для удобства // Вы можете применить typedef к структуре, для удобства.
typedef struct rectangle rect; typedef struct rectangle rect;
int area(rect r) int area(rect r)
@ -460,16 +456,6 @@ int area(const rect *r)
// Указатели на функции // Указатели на функции
/////////////////////////////////////// ///////////////////////////////////////
/*
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
*/
/* /*
Во время исполнения функции находятся по известным адресам в памяти. Во время исполнения функции находятся по известным адресам в памяти.
Указатель на функцию может быть использован для непосредственного вызова функции. Указатель на функцию может быть использован для непосредственного вызова функции.
@ -477,6 +463,7 @@ Example: use str_reverse from a pointer
Пример: использование str_reverse по указателю Пример: использование str_reverse по указателю
*/ */
void str_reverse_through_pointer(char *str_in) { void str_reverse_through_pointer(char *str_in) {
// Определение функции через указатель. // Определение функции через указатель.
void (*f)(char *); // Сигнатура должна полность совпадать с целевой функцией. void (*f)(char *); // Сигнатура должна полность совпадать с целевой функцией.
@ -491,7 +478,7 @@ void str_reverse_through_pointer(char *str_in) {
## На почитать ## На почитать
Лучше всего найдите копию [K&R, aka "The C Programming Language"](https://en.wikipedia.org/wiki/The_C_Programming_Language) Лучше всего найдите копию [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/). Другой хороший ресурс: [Learn C the hard way](http://c.learncodethehardway.org/book/).
@ -503,4 +490,4 @@ void str_reverse_through_pointer(char *str_in) {
Также не забывайте, что [Google](http://google.com) и [Яндекс](http://yandex.ru) – ваши хорошие друзья. Также не забывайте, что [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 [1] http://stackoverflow.com/questions/119123/why-isnt-sizeof-for-a-struct-equal-to-the-sum-of-sizeof-of-each-member