# С: переменные

Переменные бывают локальные (в пределах функции) и глобальные. Глобальные определяются до функции main вне остальных функций.

Переменные можно просто объявлять и дополнительно инициализировать начальное значение. Размеры типов зависят от платформы и компилятора! Вывод размера:

```c
sizeof(имя_типа);
```

**Квалификаторы типов (модификаторы)**

Они не создают новые типы, но изменяют их свойства.  
const Значение нельзя изменить const int x = 5;  
volatile Значение может измениться внешне (прерывания, hardware) volatile int status;  
restrict Указатель — единственный способ доступа к данным (для оптимизации) int \*restrict ptr;  
signed Тип со знаком (по умолчанию для char не определено) signed char c;  
unsigned Тип без знака (только положительные числа) unsigned int u;

**Псевдонимы типов (typedef)**

Позволяет дать новое имя существующему типу.

```c
typedef int my_int;           // my_int — это псевдоним для int
typedef unsigned long ulong;  // ulong — псевдоним для unsigned long

my_int x = 10;                // То же, что int x = 10;
ulong y = 100;                // То же, что unsigned long y = 100;
```

Часто используется для упрощения сложных типов:

```c
typedef struct {
    char name[50];
    int age;
} Person;                    // Теперь можно писать Person p; вместо struct Person p;
```

**Базовые типы:**

<table border="1" id="bkmrk-integer-%28int%29-%D0%A6%D0%B5%D0%BB%D1%8B%D0%B5-" style="border-collapse: collapse; width: 100%;"><colgroup><col style="width: 12.5149%;"></col><col style="width: 33.0085%;"></col><col style="width: 12.8794%;"></col><col style="width: 41.5971%;"></col></colgroup><tbody><tr><td>void</td><td>Нет значений Означает "отсутствие типа"</td><td>Нет размера</td><td>  
</td></tr><tr><td>integer (int)</td><td>Целые числа, -2,147,483,648 … 2,147,483,647

Зависит от разрядности ОС!

</td><td>4</td><td>```c++
int var;
int var=7;
```

  
</td></tr><tr><td>unsigned int</td><td>Беззнаковое целое 0 … 4,294,967,295</td><td>4</td><td>  
</td></tr><tr><td>short</td><td>Числа со знаком -32,768 … 32,767</td><td>2</td><td>  
</td></tr><tr><td>unsigned short</td><td>Беззнаковое короткое целое 0 … 65,535</td><td>2</td><td>  
</td></tr><tr><td>long</td><td>  
</td><td>4 или 8 Зависит от платформы</td><td>  
</td></tr><tr><td>unsigned long</td><td>  
</td><td>  
</td><td>  
</td></tr><tr><td>long long</td><td>Очень длинное целое</td><td>8 байт</td><td>  
</td></tr><tr><td>unsigned long long</td><td>  
</td><td>8</td><td>  
</td></tr><tr><td>float</td><td>Дробные с точностью до 7 знака</td><td>4</td><td>```c++
float var_float = 12.45;
```

  
</td></tr><tr><td>double</td><td>Дробные с точностью 15 знаков</td><td>8</td><td>  
</td></tr><tr><td>long double</td><td>  
</td><td>10, 12 или 16 Зависит от платформы</td><td>  
</td></tr><tr><td>bool</td><td>Логическое</td><td>  
</td><td>```c++
bool vr = true;
```

  
</td></tr><tr><td>char</td><td>Символ

Желательно указывать явно signed / unsigned

</td><td>1</td><td>```c++
char n = 'g';
```

  
</td></tr><tr><td>unsigned char</td><td>Беззнаковый символ 0 … 255</td><td>1</td><td>  
</td></tr></tbody></table>

**Массивы**

Хранят несколько элементов одного типа подряд в памяти. Из базовых.

```c
int numbers[10];           // Массив из 10 целых чисел
char name[50];             // Массив из 50 символов
double matrix[3][3];       // Двумерный массив (матрица 3x3)
```

**Указатели (pointer)**

Хранят адрес в памяти, где находится значение. Для обращения по адресам необходимы:

1. Возможность находить адрес, по которому в памяти располагается та или иная переменная;
2. Место, где этот адрес можно было бы сохранить для дальнейшей работы;
3. Возможность обращаться к ячейке памяти по ее адресу.

Операция получения адреса переменной &amp; (амперсанд). Хранятся адреса переменных тоже в переменных, в специальном типе,   
 переменные указатели. Количество указательных типов, равно количеству типов данных. Объявление переменной-указателя

```c
int *ptr, i;               // Указатель на int и обычный int
char *str;                 // Указатель на char (строка)
void *generic;             // Универсальный указатель (на любой тип)
```

Для изменения / получения данных из адреса оператор разыменовывания \* но в операциях с переменной:

```c
char i = 0, *p; 
p = &i;
*p = 2; 
i = i + 2; 
printf("%d %d", *p, i);
```

Но если попытаться напрямую изменить адрес, например p=p+2; то будет ошибка (такого адреса нет).

В ячейке памяти хранится адрес первой ячейки памяти. Тип переменной указывается для понимания, сколько байт нужно считывать.

**Перечисления (enum)**

Определяют набор именованных целочисленных констант.

```c
enum Color {
    RED,    // 0
    GREEN,  // 1
    BLUE    // 2
};

enum Color favorite = GREEN;
```

**Структуры**

Объединяют разные типы в один.

```c
struct Person {
    char name[50];
    int age;
    float height;
};

struct Person person1;
```

**Объединения (union)**

Как структура, но все поля используют одну и ту же область памяти (экономят память).

```c
union Data {
    int i;
    float f;
    char str[20];
};

union Data data;           // data.i, data.f, data.str занимают одну память
```

**Преобразование типов.**

Бывают явные (делает программист) и неявные (компилятор). Неявные делятся на:

1. При присваивании результата вычисления переменной, тип результата к преобразуется к типу переменной. Округление происходит не по математическим законам, а путем отбрасывания значения.
2. При вычислении выражения операнды каждой операции преобразуются к какому-то одному типу (если они различаются), при этом результат операции будет того же типа. Правило «меньший» тип к «большему», для минимизации потерю информации. Операции преобразования сначала выполняются от самого глубокого до внешнего, одного приоритета слева направо.

```c
float c = 5/6;
```

5 и 6 - целочисленные, поэтому результат целочисленный. И равен 0. Только после этого он преобразуется в float, но уже поздно. В с будет 0. А если сделать один операнд с плавающей точкой - второй преобразуется во float и дробная часть не потеряется.

```c
float c = 5.0/6;
```

Пример приоретизации операций:

```c
2 / 4 * 3,14 
// результат 0, так как одноуровневые операции слева направо
```

Явное преобразование:

```c
double result = (double)a / b;
```