[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 x_int = 0;
// shorts обычно имеет длину 2 байта
// short обычно имеет длину 2 байта
short x_short = 0;
// chars гарантированно имеет длину 1 байта
// char гарантированно имеет длину 1 байта
char x_char = 0;
char y_char = 'y'; // Символьные литералы заключаются в кавычки ''
@ -82,7 +82,7 @@ int main() {
int a = 1;
// size_t это беззнаковый целый тип который использует как минимум 2 байта
// для записи размера объекта
size_t size = sizeof(a++); // a++ считается во время компиляции
size_t size = sizeof(a++); // a++ не выполнится
printf("sizeof(a++) = %zu, где a = %d\n", size, a);
// выведет строку "sizeof(a++) = 4, где a = 1" (на 32-битной архитектуре)
@ -134,7 +134,7 @@ int main() {
// Операторы
///////////////////////////////////////
// Можно использовать множественное объявление
// Можно использовать множественное объявление.
int i1 = 1, i2 = 2;
float f1 = 1.0, f2 = 2.0;
@ -184,10 +184,9 @@ int main() {
0x02 >> 1; // => 0x01 (побитовый сдвиг вправо (на 1))
// Будьте осторожны при сдвиге беззнакового int, эти операции не определены:
// - shifting into the sign bit of a signed integer (int a = 1 << 32)
// - сдвиг в знаковый бит у целого числа (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
///////////////////////////////////////
// Структуры ветвления
@ -231,7 +230,7 @@ int main() {
// Ветвление с множественным выбором
switch (some_integral_expression) {
case 0: // значения должны быть целыми константами (могут быть выражениями)
case 0: // значения должны быть целыми константами (и могут быть выражениями)
do_stuff();
break; // если не написать break; то управление будет передено следующему блоку
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", (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);
@ -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;
printf("%p\n", (void *)&x); // => Напечатает адрес в памяти, где лежит переменная x
// (%p выводит указатель на void *)
@ -295,37 +291,37 @@ int main() {
// => Напечатает "8, 4" в 64 битной системе
// Для того, чтобы получить знаечние по адресу, напечатайте * перед именем.
// Да, использование * при объявлении указателя и получении значения по адресу,
// Да, использование * при объявлении указателя и получении значения по адресу
// немного запутано, но вы привыкнете.
printf("%d\n", *px); // => Напечаатет 0, значение перемененной x
// Вы также можете изменять значение, на которое указывает указатель.
(*px)++; // Инкрементирует значение на которое указывает px на еденицу
(*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 с адресом массива.
int* x_ptr = x_array;
// x_ptr сейчас x_ptr указывает на первый элемент массива (со значением 20).
// x_ptr сейчас указывает на первый элемент массива (со значением 20).
// Это рабоатет, потому что имя массива возвращает указатель на первый элемент.
// Например, когда массив передаётся в функцию или назначается указателю, он
// невявно преобразуется в указатель.
// Исключения: когда массив является аргументом для оператор '&':
int arr[10];
int (*ptr_to_arr)[10] = &arr; // &arr не является 'int *'!
// он является "указатель на массив" (из десяти 'int'ов).
// или когда массив это строчный литерал или при объявлении массива символов:
// он является "указателем на массив" (из десяти 'int'ов).
// или когда массив это строчный литерал, используемый при объявлении массива символов:
char arr[] = "foobarbazquirk";
// или когда массив является аргументом `sizeof` или `alignof` операторов:
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"
// Декрементация и инкрементация указателей зависит от их типа
@ -333,7 +329,7 @@ int main() {
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);
@ -347,13 +343,13 @@ int main() {
// Скорей всего программа вылетит.
// Когда вы закончили работать с памятью, которую ранее выделили, вам необходимо
// освободить её, иначе это может вызвать утечку памяти.
// освободить её, иначе это может вызвать утечку памяти или ошибки.
free(my_ptr);
// Строки это массивы символов, но обычно они представляются как
// указатели на символ (как указатели на первый элемент массива).
// Хорошей практикой считается использование `const char *' при объявлении
// строчоного литерала. При таком подходе литерал не может быть изменён.
// строчного литерала. При таком подходе литерал не может быть изменён.
// (например "foo"[0] = 'a' вызовет ошибку!)
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)
{
char tmp;
@ -409,11 +405,11 @@ printf("%s\n", c); // => Выведет ".tset a si sihT"
// Типы и структуры определяемые пользователем
///////////////////////////////////////
// typedef исапользуется для задания стандартным типам своих названий
// typedef используется для задания стандартным типам своих названий
typedef int my_type;
my_type my_type_var = 0;
// Структыры это просто коллекция данных, память выделяется последовательно,
// Структуры это просто коллекция данных, память выделяется последовательно,
// в том порядке в котором записаны данные.
struct rectangle {
int width;
@ -421,7 +417,7 @@ struct rectangle {
};
// sizeof(struct rectangle) == sizeof(int) + sizeof(int) не всегда верно
// из-за особенностей компиляции (проблема в отступах)[1].
// из-за особенностей компиляции (необычное поведение при отступах)[1].
void function_1()
{
@ -441,7 +437,7 @@ void function_1()
my_rec_ptr->height = 10; // то же что и "(*my_rec_ptr).height = 10;"
}
// Вы можете применить typedef к структуре, для удобства
// Вы можете применить typedef к структуре, для удобства.
typedef struct rectangle rect;
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 по указателю
*/
void str_reverse_through_pointer(char *str_in) {
// Определение функции через указатель.
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)
Это *книга* написанная создателями Си. Но будьте осторожны, она содержит которые больше не считаются хорошими.
Это **книга** написанная создателями Си. Но будьте осторожны, она содержит идеи которые больше не считаются хорошими.
Другой хороший ресурс: [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) – ваши хорошие друзья.
[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