С: переменные
Переменные бывают локальные (в пределах функции) и глобальные. Глобальные определяются до функции main вне остальных функций.
Переменные можно просто объявлять и дополнительно инициализировать начальное значение. Размеры типов зависят от платформы и компилятора! Вывод размера:
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)
Позволяет дать новое имя существующему типу.
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;
Часто используется для упрощения сложных типов:
typedef struct {
char name[50];
int age;
} Person; // Теперь можно писать Person p; вместо struct Person p;
Базовые типы:
| void | Нет значений Означает "отсутствие типа" | Нет размера | |
| integer (int) |
Целые числа, -2,147,483,648 … 2,147,483,647
Зависит от разрядности ОС! |
4 |
|
| unsigned int | Беззнаковое целое 0 … 4,294,967,295 | 4 | |
| short | Числа со знаком -32,768 … 32,767 | 2 | |
| unsigned short | Беззнаковое короткое целое 0 … 65,535 | 2 | |
| long | 4 или 8 Зависит от платформы | ||
| unsigned long | |||
| long long | Очень длинное целое | 8 байт | |
| unsigned long long | 8 | ||
| float | Дробные с точностью до 7 знака | 4 |
|
| double | Дробные с точностью 15 знаков | 8 | |
| long double | 10, 12 или 16 Зависит от платформы | ||
| bool | Логическое |
|
|
| char |
Символ
Желательно указывать явно signed / unsigned |
1 |
|
| unsigned char | Беззнаковый символ 0 … 255 | 1 |
Массивы
Хранят несколько элементов одного типа подряд в памяти. Из базовых.
int numbers[10]; // Массив из 10 целых чисел
char name[50]; // Массив из 50 символов
double matrix[3][3]; // Двумерный массив (матрица 3x3)
Указатели (pointer)
Хранят адрес в памяти, где находится значение. Для обращения по адресам необходимы:
- Возможность находить адрес, по которому в памяти располагается та или иная переменная;
- Место, где этот адрес можно было бы сохранить для дальнейшей работы;
- Возможность обращаться к ячейке памяти по ее адресу.
Операция получения адреса переменной & (амперсанд). Хранятся адреса переменных тоже в переменных, в специальном типе,
переменные указатели. Количество указательных типов, равно количеству типов данных. Объявление переменной-указателя
int *ptr, i; // Указатель на int и обычный int
char *str; // Указатель на char (строка)
void *generic; // Универсальный указатель (на любой тип)
Для изменения / получения данных из адреса оператор разыменовывания * но в операциях с переменной:
char i = 0, *p;
p = &i;
*p = 2;
i = i + 2;
printf("%d %d", *p, i);
Но если попытаться напрямую изменить адрес, например p=p+2; то будет ошибка (такого адреса нет).
В ячейке памяти хранится адрес первой ячейки памяти. Тип переменной указывается для понимания, сколько байт нужно считывать.
Перечисления (enum)
Определяют набор именованных целочисленных констант.
enum Color {
RED, // 0
GREEN, // 1
BLUE // 2
};
enum Color favorite = GREEN;
Структуры
Объединяют разные типы в один.
struct Person {
char name[50];
int age;
float height;
};
struct Person person1;
Объединения (union)
Как структура, но все поля используют одну и ту же область памяти (экономят память).
union Data {
int i;
float f;
char str[20];
};
union Data data; // data.i, data.f, data.str занимают одну память
Преобразование типов.
Бывают явные (делает программист) и неявные (компилятор). Неявные делятся на:
- При присваивании результата вычисления переменной, тип результата к преобразуется к типу переменной. Округление происходит не по математическим законам, а путем отбрасывания значения.
- При вычислении выражения операнды каждой операции преобразуются к какому-то одному типу (если они различаются), при этом результат операции будет того же типа. Правило «меньший» тип к «большему», для минимизации потерю информации. Операции преобразования сначала выполняются от самого глубокого до внешнего, одного приоритета слева направо.
float c = 5/6;
5 и 6 - целочисленные, поэтому результат целочисленный. И равен 0. Только после этого он преобразуется в float, но уже поздно. В с будет 0. А если сделать один операнд с плавающей точкой - второй преобразуется во float и дробная часть не потеряется.
float c = 5.0/6;
Пример приоретизации операций:
2 / 4 * 3,14
// результат 0, так как одноуровневые операции слева направо
Явное преобразование:
double result = (double)a / b;