# Глава 2. Базовый синтаксис
Вы познакомитесь с несколькими типами данных, переменными, операторами и простыми функциями. Это позволит сразу же приступить к практике. Подробнее каждая из этих тем будет раскрыта в посвященной ей главе.
## Правила именования
Правил именования в C++ всего несколько. Они распространяются на переменные, функции, классы и другие сущности в программе.
Имя _должно начинаться_ с буквы латинского алфавита или символа подчеркивания `_`: `i`, `SearchEngine`, `connect_to_db`, `_abs_val`. Символ подчёркивания в начале имени использовать [не рекомендуется:](https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#nl8-use-a-consistent-naming-style) такие имена могут оказаться зарезервированными.
Имя _может содержать_ только буквы, цифры и символы подчеркивания: `API_v3`, `isValid`, `catch2`.
Имя _не должно совпадать_ с [ключевыми словами](https://en.cppreference.com/w/cpp/keyword) языка: `int`, `if`, `union` и другими.
C++ — регистрозависимый язык. Поэтому `count`, `Count` и `COUNT` — это разные имена.
Какие имена переменных составлены правильно? Перечислите номера строк через пробел. {.task_text}
```
1 $total_volume
2 codek.meta
3 loop
4 MDFormatter
5 %TOKEN%
6 hex_val
```
```consoleoutput {.task_source #cpp_chapter_0020_task_0010}
```
Имя `$total_volume` содержит недопустимый символ `$`. Имя `codek.meta` содержит недопустимую в названии точку. Имя `%TOKEN%` содержит недопустимый символ `%`. {.task_hint}
```cpp {.task_answer}
3 4 6
```
## Правила форматирования
В C++ отсутствуют общепринятые правила форматирования. Например, нет разницы между пробелами и табуляцией, а наличие отступов опционально. Фигурные скобки можно ставить на любой строке.
Перед вами два разных подхода к форматированию:
```c++ {.example_for_playground}
int main() {
int x = 5 + (2 - 1);
}
```
```c++ {.example_for_playground}
int main()
{
int x=5+(2-1);
}
```
Более того, вся программа может быть записана в одну строку, оставаясь при этом корректной. Хоть и не читабельной.
## Точка входа в программу
Функция с именем `main` — это точка входа в программу (entry point). Ее наличие обязательно: после запуска программы управление передается именно ей.
Так выглядит минимальная программа на C++, которая ничего не делает:
```c++ {.example_for_playground}
int main() { }
```
Функция `main()` возвращает [целое число](https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#Rf-main) типа `int` вызвавшему программу окружению. Это статус завершения:
- 0 в случае успеха,
- другое значение в случае ошибки.
В нашем примере тело функции пустое: `{ }`. Но как же тогда формируется статус завершения? Функция `main()` — особая: при отсутствии явно возвращаемого значения она возвращает 0. Для наглядности мы можем вернуть его явно:
```c++ {.example_for_playground}
int main()
{
return 0;
}
```
Чтобы обеспечить выполнение кода, удостоверьтесь, что он вызывается из функции `main()`.
## Функции
При объявлении функции сначала указывается тип возвращаемого значения, потом имя функции, после него параметры. А затем тело функции, обрамленное фигурными скобками:
 {.illustration}
Напомним, что **параметр** — это имя в определении функции. А **аргумент** — это фактическое значение, переданное функции при вызове.
Рассмотрим реализацию функции `is_error()` и ее вызов:
```c++ {.example_for_playground}
import std;
bool is_error(int http_code)
{
return http_code >= 300;
}
int main()
{
bool res = is_error(404);
std::println("404 is error code? {}", res);
return 0;
}
```
```
404 is error code? true
```
Для возврата из функции значения мы использовали [оператор](https://en.wikipedia.org/wiki/Operator_(computer_programming)) `return`.
А для вывода `res` в консоль мы сделали две вещи:
- Импортировали стандартную библиотеку `std`. В ней содержится функция [println()](https://en.cppreference.com/w/cpp/io/println), отвечающая за форматированный вывод.
- Вызвали `println()`. Она находится в пространстве имен (namespace) `std`, и мы указали его при вызове: `std::println()`.
Вы обратили внимание, что некоторые строки в программе заканчиваются точкой с запятой? Это **инструкции** (statements) — фрагменты кода, выполняемые последовательно. {#block-statements}
Напишите функцию `to_fahrenheit()`, которая: {.task_text}
- Принимает вещественное число типа `double` — температуру в градусах по Цельсию.
- Возвращает градусы по шкале Фаренгейта (`double`). Формула: `°F = °C × 9.0/5.0 + 32.0`. Чтобы ее реализовать, воспользуйтесь операторами для сложения `+`, умножения `*` и деления `/`. {.task_text}
```c++ {.task_source #cpp_chapter_0020_task_0020}
```
Возвращаемое функцией значение, если параметр называется `celsius`: `celsius * 9.0 / 5.0 + 32.0`. {.task_hint}
```c++ {.task_answer}
double to_fahrenheit(double celsius)
{
return celsius * 9.0 / 5.0 + 32.0;
}
```
## Переменные
Чтобы создать переменную, укажите ее тип и имя. А затем через оператор `=` проинициализируйте значением:
```c++
int request_count = 0;
```
После типа можно перечислять несколько переменных, разделенных запятой:
```c++
int left = -100, right = 100;
```
Однако делать так [не рекомендуется:](https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#Res-name-one) такой код сложно читать. Лучше заводите по одной переменной на одну строку:
```c++
int left = -100;
int right = 100;
```
В некоторых языках действует правило: если переменной не задано значение явно, то она инициализируется значением по умолчанию. C++ к таким языкам не относится:
```c++
int request_count; // Здесь может быть что угодно!
```
Поэтому при создании переменной [обязательно задавайте ей значение.](https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#es20-always-initialize-an-object) {#block-initialization}
Чтобы изменить значение переменной, применяется уже знакомый вам **оператор присваивания** (assignment operator):
```c++
double default_len = 6.7;
double len = default_len;
len = len + 2; // 8.7
```
Чему равны значения `a` и `b`? Введите их через пробел. {.task_text}
```c++ {.example_for_playground .example_for_playground_004}
int a = -1
int b = 4;
int c = a;
a = b;
b = c;
```
```consoleoutput {.task_source #cpp_chapter_0020_task_0030}
```
В этом коде значения переменных `a` и `b` меняются местами с использованием переменной `c`. {.task_hint}
```cpp {.task_answer}
4 -1
```
## Константы
Делать константами все переменные, которые не требуется изменять — это [отличная практика.](https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#Rconst-immutable) Она предотвращает случайную перезапись переменной.
Константы помечаются квалификатором типа [const](https://en.cppreference.com/w/cpp/language/cv). Попытка перезаписи константы приведет к ошибке компиляции. Квалификатор `const` может стоять как слева от типа, так и справа:
```c++
const int equator_len_km = 40075;
int const winter_avg_temp = -5;
```
## Знакомство с фундаментальными типами
[Фундаментальные типы](https://en.cppreference.com/w/cpp/language/types#Standard_floating-point_types) (fundamental types) — это типы, встроенные в язык. Их имена являются ключевыми словами (keywords). Рассмотрим некоторые из них:
- `int` — знаковое целое: `93`, `-3`, `0`, `9'100`.
- `double` — число с плавающей точкой [двойной точности:](https://en.wikipedia.org/wiki/Double-precision_floating-point_format) `-45.5`, `1e6`, `0.0`, `NAN` (not a number), `INFINITY`.
- `bool` — логическое значение: `true`, `false`.
- `char` — ASCII-символ: `'a'`, `'9'`, `'\t'`, `50`.
- `void` — отсутствие значения.
### Типы int и double
Большие числовые значения удобно разбивать по разрядам символом штриха `'`:
```c++
int avg_dist_to_moon_km = 384'400;
```
В [литералах](https://en.wikipedia.org/wiki/Literal_(computer_programming)) типа `double` целая часть отделяется от дробной точкой.
```c++
double weight = 1008.9;
```
Тип `double` поддерживает экспоненциальную запись числа. Она удобна для компактного представления длинных значений.
```c++
double a = 3e6; // 3x10^6 = 3'000'000.0
double b = -7e-2; // -7x10^-2 = -0.07
```
Напишите экспоненциальное представление числа 0.00002. {.task_text}
Если вы раньше не работали с экспоненциальной записью, самое время [разобраться в ней.](https://urvanov.ru/2021/12/08/%D0%BD%D0%B0%D1%83%D1%87%D0%BD%D0%B0%D1%8F-%D0%B7%D0%B0%D0%BF%D0%B8%D1%81%D1%8C-%D1%87%D0%B8%D1%81%D0%BB%D0%B0/) {.task_text}
```consoleoutput {.task_source #cpp_chapter_0020_task_0040}
```
Представим число 0.00002 в виде мантиссы и порядка. Мантисса: 2. Порядок: -5. {.task_hint}
```cpp {.task_answer}
2e-5
```
### Тип bool
Логический тип `bool` может принимать два значения: `true` и `false`.
```c++
bool is_eq = false;
bool has_open_connections = true;
```
### Тип char
Переменную символьного типа `char` можно инициализировать символом в одинарных кавычках:
```c++
char letter = 'S';
```
А можно кодом символа из [ASCII-таблицы:](https://www.asciitable.com/)
```c++
char letter = 83;
```
Тип `char` представляет собой целое число, которое _можно_ трактовать как ASCII-код. Поэтому в обоих примерах переменная `letter` содержит одно и то же значение — число 83, в ASCII-таблице соответствующее заглавной букве S латинского алфавита.
### Тип void
Используйте тип `void` в качестве типа возвращаемого значения функции, если она ничего не возвращает:
```c++ {.example_for_playground}
void show_warning()
{
std::println("Something went wrong");
}
```
Кстати, вызывать `return` в конце такой функции не обязательно. Но его можно использовать для раннего выхода (early exit):
```c++
if (!is_connection_opened)
{
return;
}
```
## Знакомство с библиотечными типами
Итак, мы обсудили несколько встроенных в язык типов. А теперь взглянем на два типа из стандартной библиотеки C++. Они пригодятся вам уже в следующей главе:
- `std::size_t` — беззнаковое целое.
- `std::string` — класс, реализующий строку.
Класс — это пользовательский тип данных, призванный объединять данные (поля класса) и методы по работе с ними.
### Тип std::size_t
Тип [std::size_t](https://en.cppreference.com/w/cpp/types/size_t) может хранить:
- Индекс элемента в контейнере. [Контейнер](https://en.cppreference.com/w/cpp/container) — это коллекция элементов. Например, переменная типа `std::size_t` может хранить индекс символа строки.
- Длину контейнера.
- Размер объекта в байтах.
- Счетчик цикла.
Под капотом `std::size_t` — псевдоним (alias) для одного из фундаментальных беззнаковых целых типов.
```c++
import std;
int main()
{
const std::size_t i = 9;
std::println("{}", i);
}
```
```
9
```
### Класс std::string
Тип [std::string](https://en.cppreference.com/w/cpp/string/basic_string) реализует строку, не привязанную к кодировке. Она представляет собой последовательность символов типа `char`.
Если `std::size_t` — всего лишь псевдоним фундаментального типа, то `std::string` — полноценный класс, содержащий методы для работы со строкой.
```c++
import std;
int main()
{
std::string s = "The standard string class";
// Получение символа по его индексу:
const char c = s[1];
// Длина строки:
const std::size_t n = s.size();
// Запись символа по индексу:
s[n-1] = 'S';
std::println("{}", s);
std::println("{} {}", c, n);
}
```
```
The standard string clasS
h 25
```
Из примера видно, что строковый литерал заключается в двойные кавычки. А для обращения к символу строки по индексу используется оператор `[]`. Индексация начинается с нуля.
Чтобы получить размер строки, мы вызвали метод `size()`. Для вызова метода между объектом класса и именем метода ставится точка: `s.find("st")`. Точка — это тоже оператор, и он нужен для доступа к членам класса (то есть его полям и методам).
У класса `std::string` есть [множество](https://en.cppreference.com/w/cpp/string/basic_string) полезных методов. Вот некоторые из них с примерами для строки `s="example"`:
- `size()` возвращает длину строки: `s.size() // 7`.
- `empty()` возвращает `true`, если строка пустая: `s.empty() // false`.
- `insert()` вставляет заданное количество символов по указанному индексу: `s.insert(1, 2, '8') // e88xample`.
- `contains()` проверяет, присутствует ли в строке подстрока или символ: `s.contains("am") // true`, `s.contains('y') // false`. Этот метод появился в C++23.
## Операторы {#block-operators}
Вы уже познакомились с [оператором присваивания](https://en.cppreference.com/w/cpp/language/operator_assignment) `=`, оператором взятия элемента по индексу `[]` и оператором доступа к члену класса `.`. Рассмотрим еще несколько категорий операторов, которые пригодятся в первую очередь.
Чтобы поменять приоритет выполнения операторов, они группируются скобками.
### Операторы сравнения
[Операторы сравнения](https://en.cppreference.com/w/cpp/language/operator_comparison) (comparison operators) применимы к большинству фундаментальных типов:
- `==` — равенство.
- `!=` — неравенство.
- `<`, `>` — меньше, больше.
- `<=`, `>=` — меньше или равно, больше или равно.
Выражения сравнения приводятся к типу `bool`. **Выражение** (expression) — это последовательность операторов и операндов.
```c++
bool a = 8.1 < 16; // true
bool b = -5 != -5; // false
std::string s = "";
bool c = s.empty(); // true
bool d = s.size() == 4; // false
```
Чему равно значение `b`? {.task_text}
```c++ {.example_for_playground .example_for_playground_005}
std::string text = "Operator";
bool b = text[text.size() - 1] == text[3];
```
```consoleoutput {.task_source #cpp_chapter_0020_task_0090}
```
`text.size()` вернет 8. Символ по индексу 7 равен `r`. Символ по индексу 3 тоже равен `r`. {.task_hint}
```cpp {.task_answer}
true
```
### Логические операторы
[Логические операторы](https://en.cppreference.com/w/cpp/language/operator_logical) применимы ко всем выражениям, которые приводятся к `bool`:
- `&&` — «И»: `is_filled && is_valid`.
- `||` — «ИЛИ»: `has_gps_location || connected_to_wifi`.
- `!` — «НЕ» (отрицание): `!is_valid`.
```c++ {.example_for_playground .example_for_playground_001}
bool is_online = true;
bool is_updated = false;
std::println("{}", is_online || is_updated); // true
std::println("{}", !(is_online && is_updated)); // true
```
XOR — это булева функция, также известная как [исключающее «ИЛИ».](https://ru.wikipedia.org/wiki/%D0%98%D1%81%D0%BA%D0%BB%D1%8E%D1%87%D0%B0%D1%8E%D1%89%D0%B5%D0%B5_%C2%AB%D0%B8%D0%BB%D0%B8%C2%BB) Она принимает два флага и возвращает `true`, если один из них истинен, а другой — ложен. В остальных случаях она возвращает `false`. {.task_text}
Напишите свою реализацию `hello_xor()`. {.task_text}
```c++ {.task_source #cpp_chapter_0020_task_0050}
```
Функция возвращает `true` тогда и только тогда, когда один из аргументов равен `true`, а другой — `false`. {.task_hint}
```c++ {.task_answer}
bool hello_xor(bool a, bool b)
{
return (!a && b) || (a && !b);
}
```
### Арифметические операторы
[Арифметические операторы](https://en.cppreference.com/w/cpp/language/operator_arithmetic) (arithmetic operators) применимы к любым выражениям, которые приводятся к числам. Они позволяют осуществлять:
- `+` — сложение: `5 + 6 == 11`.
- `-` — вычитание. `8 - 9 == -1`.
- `*` — умножение. `3 * 7 == 21`.
- `/` — деление. `10 / 4 == 2`.
- `%` — деление по модулю, то есть получение остатка от деления. `11 % 3 == 2`.
Перечисленные операторы называются бинарными. Они применяются к двум операндам: `a + b`. В C++ есть и унарные операторы — унарные плюс и минус: `+a`, `-a`.
### Составное присваивание {#block-compound-assignment}
[Операторы составного присваивания](https://en.cppreference.com/w/cpp/language/operator_assignment) (compound assignment) объединяют присваивание переменной с арифметическим действием над ней. Их ввели в язык, чтобы записывать простые арифметические действия более кратко:
```c++
x += 5; // x = x + 5
x -= y; // x = x - y
x *= 10; // x = x * 10;
x /= y; // x = x / y;
x %= 2; // x = x % 2;
```
### Инкремент и декремент
Увеличение или уменьшение значения на единицу можно записывать еще короче! [Оператор инкремента](https://en.cppreference.com/w/cpp/language/operator_incdec) `++` увеличивает значение на 1, а оператор декремента `--` уменьшает. Эти операторы применимы _только_ к целым числам.
```c++
++x; // Эквивалентно x+=1
--x; // Эквивалентно x-=1
```
В этом примере приведена префиксная форма операторов: пре-инкремент и пре-декремент.
Есть и постфиксная форма: в ней `++` и `--` указываются после переменной. Это называется пост-инкрементом и пост-декрементом:
```c++
x++;
x--;
```
Обе формы изменяют переменную и возвращают ее значение. Разница в _очередности_ этих действий.
Префиксный оператор сначала изменяет переменную на 1, а потом возвращает значение:
```c++
a = 2;
b = ++a; // a=3, b=3
```
Постфиксный оператор сначала возвращает значение переменной, и лишь затем увеличивает ее на 1:
```c++
a = 2;
b = a++; // a=3, b=2
```
Постфиксные операторы инкремента и декремента имеют более высокий приоритет, чем префиксные.
И еще одно важное отличие: префиксные формы возвращают саму переменную, а постфиксные — ее неизменяемую копию. Поэтому такой код не скомпилируется:
```c++
--i++;
```
Вначале выполнится постфиксный оператор и вернет неизменяемую копию переменной: `--(i++)`. А так как неизменяемое значение нельзя уменьшить, компилятор прервет сборку программы с ошибкой:
```
error: expression is not assignable
--i++;
^ ~~~
```
### Приоритет операторов
Операторы с равным приоритетом применяются слева направо. Порядок вычисления можно изменять с помощью скобок.
Перечислим уже знакомые вам операторы по убыванию приоритета. Если в строке несколько операторов, то приоритет у них одинаковый.
- `a::b` — разрешение области видимости, например `std::println()`. Это оператор с наивысшим приоритетом.
- `a++`, `a--`, `a[b]`, `a.b` — постфиксные инкремент и декремент, взятие по индексу, доступ к члену класса.
- `!a`, `+a`, `-a`, `++a`, `--a` — логическое отрицание, унарные плюс и минус, префиксные инкремент и декремент.
- `a * b`, `a / b`, `a % b` — умножение, деление, модуль числа.
- `a + b`, `a - b` — сложение, вычитание.
- `a < b`, `a <= b`, `a > b`, `a >= b` — больше, меньше.
- `a == b`, `a != b` — равенство, неравенство.
- `a && b` — логическое «И».
- `a || b` — логическое «ИЛИ».
- `a = b`, `a += b`, `a -= b`, `a *= b`, `a /= b`, `a %= b` — присваивание, составное присваивание.
Таблицу с приоритетом _всех_ операторов C++ вы можете [посмотреть на cppreference.](https://en.cppreference.com/w/cpp/language/operator_precedence)
Нужны ли скобки, чтобы это выражение вычислилось как ожидается? `y/n`. {.task_text}
```c++
width < 0 || volume / length <= max_val
```
```consoleoutput {.task_source #cpp_chapter_0020_task_0060}
```
Приоритет деления `/` выше, чем сравнения `<=`. А приоритет `||` меньше, чем сравнения. {.task_hint}
```cpp {.task_answer}
n
```
Как быть, если вы сомневаетесь, нужны ли в выражении скобки? [Если без скобок код трудно читать, то ставьте их!](https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#Res-parens) Например, выражение из задачи выше со скобками выглядит проще: `(width < 0) || (volume / length <= max_val)`.
Какое значение у переменной `x`? {.task_text}
В случае ошибки напишите `err`. {.task_text}
Ремарка: это пример плохого кода. В реальных проектах избегайте подобных трудночитаемых конструкций. Однако они встречаются на собеседованиях. {.task_text}
```c++ {.example_for_playground .example_for_playground_002}
int a = 1, b = 2, c = 3;
int x = a-- - b++ - c--;
```
```consoleoutput {.task_source #cpp_chapter_0020_task_0070}
```
Приоритет постфиксных операторов выше, чем у оператора вычитания `-`. Постфиксный оператор сначала возвращает значение переменной, а потом изменяет его. Поэтому `x` равен `1 - 2 - 3`. {.task_hint}
```cpp {.task_answer}
-4
```
Что будет выведено в консоль? {.task_text}
В случае ошибки напишите `err`. {.task_text}
```c++ {.example_for_playground .example_for_playground_003}
int c = 2;
int C = 5;
std::print("{}", c++ * ++C);
```
```consoleoutput {.task_source #cpp_chapter_0020_task_0080}
```
В этом выражении у постфиксного оператора максимальный приоритет. Следующим по приоритету выполнится префиксный оператор. И лишь затем — оператор умножения. Мы получим `2 * 6`. {.task_hint}
```cpp {.task_answer}
12
```
----------
## Резюме
- Функция `main()` — это точка входа в программу.
- Если функция ничего не возвращает, то тип ее возвращаемого значения `void`.
- При создании переменных всегда инициализируйте их значением.
- Неизменяемые переменные помечаются квалификатором типа `const`.
- Несколько фундаментальных типов: `bool`, `int`, `double`, `char`, `void`.
- Пара библиотечных типов: `std::size_t`, `std::string`.
- Операторы сравнения: `==`, `!=`, `<`, `>`, `<=`, `>=`.
- Логические операторы: `&&`, `||`, `!`.
- Арифметические операторы: `+`, `-`, `*`, `/`, `%`.
- Операторы составного присваивания — это краткая форма выполнения над переменной арифметического действия и присваивания ей. Например, `x *= 2`.
- Операторы инкремента `++` и декремента `--` бывают префиксными и постфиксными.
Наша группа в telegram. Здесь можно задавать вопросы и общаться.
Задонатить. Если вам нравится курс, вы можете поддержать развитие площадки!