Содержание29 разделов
В прошлой статье мы написали программу, которая выводит на экран строку «Hello, world!». Она ничего не запоминала и ничего не вычисляла: текст был записан прямо в коде.
Настоящие программы работают с данными: возрастом пользователя, ценой товара, координатами объекта на экране, ответом «да» или «нет». Чтобы хранить такие данные, в C++ используются переменные, а чтобы описать, что именно в них хранится, — типы данных.
В этой статье мы разберём, какие бывают типы данных, сколько памяти они занимают, как получить данные от пользователя и как выполнять над ними арифметические операции.
После этой статьи Вы сможете:
- объяснить, что такое переменная и из чего она состоит;
- назвать основные типы данных C++ и их модификаторы:
int,double,char,boolи другие; - сказать, сколько байт занимает тип и какие значения в нём можно хранить;
- объявить переменную и дать ей понятное имя;
- считывать данные с клавиатуры через
std::cinи выводить их в терминал черезstd::cout; - выполнять арифметические операции и сокращать запись с помощью
+=,++и подобных операторов; - преобразовывать значение одного типа в другой с помощью
static_cast.
Что такое переменная?
Начнём с простой аналогии. Переменная - это некоторый ящик, который имеет название и хранит некоторое значение. Приведем несколько примеров.
На рисунке три переменные: age хранит число 23, pi — число 3.1415, а name — текст "Marcus". Значениями переменных можно оперировать: выводить их на экран, складывать, умножать, делить, увеличивать и уменьшать.
В прошлой статье мы выяснили, что C++ — статически типизированный язык. Это значит, что, объявляя переменную, программист обязан указать её тип данных. Тип сообщает компилятору, какие значения может хранить переменная, сколько памяти для неё выделить и какие операции с ней разрешены.
Основные типы данных C++
В C++ всего четыре базовых, или примитивных, типа данных. Из них строится всё многообразие переменных и контейнеров, с которыми мы будем работать дальше.
- Целочисленный тип хранит только целые числа: 15, -10, 0, 2 153 988.
- Дробный тип, который также называют вещественным или типом с плавающей запятой, хранит числа с дробной частью: 3.14, 2.71, -0.00007. Обратите внимание: в C++ дробную часть отделяют точкой, а не запятой.
- Символьный тип хранит один символ: букву
'a', пробел' ', восклицательный знак'!'или символ перевода строки'\n', с которым мы познакомились в прошлой статье. - Логический тип хранит одно из двух значений:
true(истина) илиfalse(ложь). При необходимости они превращаются в числа 1 и 0.
Похожий набор базовых типов есть в большинстве языков программирования, поэтому эти знания пригодятся Вам и за пределами C++.
Модификаторы типов данных
Чтобы объявить переменную определённого типа, в C++ используют ключевые слова. У одного типа данных может быть несколько вариантов записи, и в этом учебнике мы будем называть их модификаторами типа.
Строго говоря, модификаторами обычно называют только слова short, long, signed и unsigned, которые изменяют базовый тип int, а short int, int и long long int — это разные типы. Но на старте удобнее думать о них как о вариантах записи одного целочисленного типа.
Целочисленные типы: short int, int и long long int
У целочисленного типа три основных модификатора: short int, int и long long int. Чтобы объявить переменную, которая хранит целое число, нужно выбрать один из них.
Зачем три модификатора для одного типа данных? Каждый из них хранит свой диапазон значений и занимает разный объём памяти:
| Модификатор | Диапазон хранимых значений | Объём памяти (байт) |
|---|---|---|
short int | -32'768 … 32'767 | 2 |
int | -2'147'483'648 … 2'147'483'647 | 4 |
long long int | -9'223'372'036'854'775'808 … 9'223'372'036'854'775'807 | 8 |
Получается, что short int хранит числа примерно от −32 тысяч до 32 тысяч, int — примерно от −2 миллиардов до 2 миллиардов, а диапазон long long int огромен: примерно ±9,2 квинтиллиона, то есть 9,2 · 10¹⁸.
Возникает вопрос: зачем нужны short int и int, если можно всегда использовать long long int? В него ведь помещается больше всего чисел.
Дело в том, что за большой диапазон приходится платить памятью. Чем больше значений может хранить модификатор, тем больше места переменная занимает в оперативной памяти компьютера.
Например, в 1 ГБ оперативной памяти (1 073 741 824 байта) поместится примерно 268 миллионов переменных типа int, но лишь 134 миллиона переменных типа long long int — в два раза меньше. А по сравнению с short int переменных long long int поместится в четыре раза меньше. Поэтому модификатор стоит выбирать с умом.
Как выбрать модификатор
Как же выбрать подходящий модификатор? Подумайте о предметной области — о том, какие значения на самом деле будет хранить переменная:
- возраст человека не может превышать 32 тысячи лет, поэтому для него подойдёт
short int; - количество граммов картошки, которую человек купил на рынке, лучше хранить в
int: больше 32 килограммов купить можно, а больше 2 тысяч тонн — вряд ли, если Вы не оптовик; - для населения Земли нужен
long long int: больше 8 миллиардов человек вintуже не поместится.
Так Вы будете расходовать память компьютера максимально разумно.
Беззнаковые типы: unsigned
К каждому целочисленному модификатору можно добавить ключевое слово unsigned — «беззнаковый». Получаются unsigned short int, unsigned int и unsigned long long int.
Беззнаковые типы не хранят отрицательные числа: всё, что было в отрицательной части диапазона, «переезжает» в положительную.
| Модификатор | Диапазон хранимых значений | Объём памяти (байт) |
|---|---|---|
unsigned short int | 0 … 65'535 | 2 |
unsigned int | 0 … 4'294'967'295 | 4 |
unsigned long long int | 0 … 18'446'744'073'709'551'615 | 8 |
Количество возможных значений при этом не меняется, поэтому беззнаковые типы занимают столько же памяти, сколько их обычные версии.
Беззнаковые типы полезны, когда значение по смыслу не может быть отрицательным. Например, это возраст или размер массива — количество элементов в нём. С массивами мы познакомимся позже: в массиве может быть от нуля до очень большого количества элементов, а отрицательный размер не имеет смысла.
Вещественные типы: float, double и long double
Дробные числа хранятся в переменных вещественного типа. У него три модификатора: float, double и long double.
| Модификатор | Диапазон хранимых значений | Объём памяти (байт) |
|---|---|---|
float | 1.17549e-38 … 3.40282e+38 | 4 |
double | 2.22507e-308 … 1.79769e+308 | 8 |
long double | 2.22507e-308 … 1.79769e+308 | 8 |
В таблице указаны наименьшее и наибольшее положительные значения в экспоненциальной записи: 3.40282e+38 означает 3,40282 · 10³⁸. Отрицательные числа хранятся в таком же диапазоне со знаком минус.
Главное отличие между модификаторами — точность, то есть сколько значащих цифр число хранит без искажений. float хранит примерно 6–7 значащих цифр, а double — примерно 15–16.
Если высокая точность не нужна, например Вы храните температуру воздуха, можно использовать float. Для точных вычислений, например при расчёте траектории полёта ракеты, нужен самый точный тип.
Обратите внимание, что в таблице у double и long double одинаковые диапазон и размер. Так устроено на macOS с процессорами Apple, где записывался видеоурок, и в компиляторе MSVC под Windows: там long double совпадает с double. Но это разные типы, и на других платформах они различаются. Например, в GCC на Linux с процессором x86-64 long double занимает 16 байт и точнее, чем double.
На практике чаще всего используют double: он достаточно точный и занимает 8 байт. Если не уверены, какой тип выбрать для дробного числа, — выбирайте double.
Символьный и логический типы: char и bool
С этими типами мы подробно поработаем через несколько уроков, а пока познакомимся с их характеристиками.
| Тип данных | Модификатор | Диапазон хранимых значений | Объём памяти (байт) |
|---|---|---|---|
| Символьный | char | -128 … 127 | 1 |
| Логический | bool | true, false | 1 |
Почему диапазон символьного типа записан числами? Дело в том, что компьютер хранит символы в виде чисел — кодов. Соответствие между символами и кодами задаёт таблица ASCII: например, у буквы 'a' код 97, а у пробела — 32.
Обычно char хранит значения от -128 до 127, но на некоторых платформах он беззнаковый и хранит значения от 0 до 255.
Тип bool хранит всего два значения, и для этого хватило бы одного бита. Тем не менее он занимает целый байт: в C++ не бывает переменных меньше одного байта, потому что байт — это минимальная единица памяти, к которой компьютер может обратиться напрямую.
Сводная таблица типов данных
Соберём все модификаторы в одну таблицу. Её можно сохранить и использовать как шпаргалку:
Запоминать всю таблицу не нужно. В большинстве задач Вам хватит четырёх модификаторов:
int— для целых чисел;double— для дробных чисел;char— для символов;bool— для логических значений.
К остальным модификаторам Вы будете обращаться, когда почувствуете силы думать не только о решении задачи, но и о выборе самого подходящего типа.
Объявление и инициализация переменных
Посмотрим, как объявлять переменные в коде. Сначала указывается тип данных переменной (а точнее, модификатор), затем её имя. После имени можно сразу поставить точку с запятой или указать начальное значение:
// <тип> <название>;
// <тип> <название> = <значение>;
// <тип> <название>{<значение>};
// <тип> <название>(<значение>);
Указание начального значения при объявлении называется инициализацией. Объявим переменные с тремя модификаторами целочисленного типа, которые мы уже изучили:
#include <iostream>
int main() {
short int age = 23;
int money_amount = 1'000'000;
int arbuz = 12345;
long long int ananas = -9123;
return 0;
}
Все четыре строки устроены одинаково: сначала тип, затем имя переменной, знак = и значение. Тип указывать обязательно — без него компилятор не поймёт, сколько памяти выделить под переменную и что с ней можно делать.
Переменные без начального значения
В одной строке можно объявить сразу несколько переменных одного типа. Их имена перечисляют через запятую:
int a, b;
std::cout << a << " " << b;
Между значениями мы выводим пробел. Пробел — это один символ, поэтому его можно записать и в одинарных кавычках ' ', и в двойных " ".
Здесь мы не указали начальные значения, поэтому переменные остались неинициализированными. В них окажется «мусор» — случайные данные, которые до этого лежали в памяти компьютера. Если запустить программу, можно увидеть, например, такой результат:
-316981160 1
У Вас значения почти наверняка будут другими. CLion подсвечивает такие переменные и предупреждает, что они не инициализированы.
Исправим пример и сразу зададим переменным значения:
int a{4}, b = 3;
std::cout << a << " " << b;
4 3
Обратите внимание: значение переменной a мы указали в фигурных скобках, а переменной b — через знак =. Для таких простых переменных оба способа дают одинаковый результат. Запись в круглых скобках, например int a(4);, тоже работает, но встречается реже.
Скотт Майерс, один из самых известных экспертов по C++, в книге «Эффективный и современный C++» подробно разбирает, чем отличаются эти способы. Но различия проявляются в довольно специфических ситуациях, поэтому новичку пока не стоит забивать ими голову.
Одно полезное отличие всё же стоит запомнить: фигурные скобки не позволяют незаметно потерять данные. Например, строка int x{3.7}; не скомпилируется, а строка int x = 3.7; скомпилируется и сохранит в x число 3 — в лучшем случае компилятор выдаст предупреждение.
В этом учебнике мы чаще будем писать через знак =: такая запись привычна и хорошо читается.
Как называть переменные
В именах переменных можно использовать:
- латинские буквы — строчные и заглавные;
- цифры;
- символ нижнего подчёркивания
_.
short int age123 = 23;
short int AGE123 = 23;
int money___a__m__ount_ = 1'000'000;
int ar123b4u512z231 = 12345;
C++ — регистрозависимый язык. Переменные age123 и AGE123 различаются регистром букв, поэтому это две разные переменные.
При этом нужно соблюдать несколько правил:
- имя не может начинаться с цифры: строка
int 1a = 5;вызовет ошибку компиляции; - нижнее подчёркивание можно ставить в середине или в конце имени, но не в начале. Такие имена используются для макросов и внутренних деталей стандартной библиотеки, а часть из них зарезервирована языком;
- имя не может совпадать с ключевым словом языка, например
intилиreturn.
Также не стоит называть переменные русскими буквами:
long long int арбуз = -9123;
Некоторые компиляторы разрешают так писать, но полагаться на это не стоит: такой код ненадёжен, его неудобно набирать и поддерживать. Используйте латиницу и давайте переменным понятные имена: age, money_amount, student_count.
Разделители разрядов
Когда в числе много цифр, его трудно прочитать. Поэтому, начиная с C++14, разряды числа можно разделять апострофом ':
int a = 1000000;
int b = 1'000'000;
Обе переменные хранят одно и то же число — миллион. Апострофы нужны только для удобства чтения и не влияют на значение.
Переменные других типов
Переменные остальных типов, с которыми мы познакомились выше, объявляются точно так же:
double a;
float g = 12.3;
char sym = 'a';
bool flag = true;
Принцип всегда одинаковый: модификатор, имя переменной, а затем значение или точка с запятой.
Ввод и вывод: std::cin и std::cout
Теперь научимся получать данные от пользователя. Для ввода с клавиатуры используется std::cin. Его название удобно запомнить как console input — ввод с консоли. Как и std::cout, он объявлен в заголовке <iostream>.
#include <iostream>
int main() {
int a, b;
std::cin >> a; // console input
std::cin >> b; // console input
std::cout << a + b;
return 0;
}
Оператор >> передаёт введённое значение в переменную. Программа ждёт, пока пользователь введёт два числа, а затем выводит их сумму. Арифметические операторы мы ещё не проходили, но знак + здесь работает так же, как в математике.
Запустим программу и введём числа через пробел:
1 2
3
Числа можно ввести и на разных строках, нажимая Enter после каждого:
1
2
3
Более того, std::cin пропускает все пробелы и переводы строк перед числом. Даже если ввести много пробелов и несколько раз нажать Enter, числа всё равно считаются правильно:
4
7
11
Это может быть непривычно, если Вы раньше изучали Python: там функция input() читает строку целиком, и разбирать её на числа приходится отдельно.
Несколько переменных можно считать и одной строкой — для этого операторы >> записывают цепочкой:
#include <iostream>
int main() {
int a, b;
std::cin >> a >> b; // console input
std::cout << a + b;
return 0;
}
Переменные в такой цепочке могут быть любого из изученных типов: int, double, float и других. Правила остаются прежними: значения можно разделять пробелами или переводами строк.
На первых уроках мы чаще всего будем считывать int и double, реже — char. А переменные типа bool с клавиатуры почти никогда не вводят.
Арифметические операторы
Переходим к следующей теме урока. В C++ есть пять арифметических операторов:
| Обозначение | Наименование | Пример | Результат |
|---|---|---|---|
+ | сложение | 5 + 2 | 7 |
- | вычитание | 5 - 2 | 3 |
/ | деление | 5 / 2 | 2 |
/ | деление | 3.14 / 2.0 | 1.57 |
* | умножение | 5 * 2 | 10 |
% | извлечение остатка | 5 % 2 | 1 |
Сложение, вычитание и умножение работают так же, как в математике. А вот деление и оператор % разберём подробнее.
Целочисленное и вещественное деление
В C++ есть два вида деления. Если делить целое число на целое, то есть int на int, результат тоже будет целым:
std::cout << 5 / 2; // 2
Математически 5 / 2 = 2,5, и по правилам округления получилось бы 3. Но C++ не округляет результат целочисленного деления, а отбрасывает дробную часть, поэтому мы получаем 2. Для отрицательных чисел дробная часть тоже просто отбрасывается: -5 / 2 даст -2.
Если делить дробные числа, результат будет дробным:
std::cout << 3.14 / 2.0; // 1.57
Что будет, если разделить целое число на дробное, разберём в конце урока, когда дойдём до приведения типов.
Остаток от деления: оператор %
Оператор % вычисляет остаток от деления. Именно он обычно вызывает больше всего вопросов у новичков, поэтому разберём его на примере.
Представьте, что у Вас есть 5 новых телефонов, которые все хотят получить, и 2 лучших друга — назовём их Ваня 1 и Ваня 2. Вы любите и цените друзей одинаково и хотите раздать им телефоны так, чтобы каждый получил как можно больше и при этом поровну. Разделить телефон пополам, конечно, нельзя.
Каждый друг получит по 2 телефона, и у Вас останется 1 телефон. Это и есть результат операции 5 % 2: столько остаётся после того, как мы раздали поровну максимально возможное количество.
Немного практики в извлечении остатка
7 % 12 // 7
6 % 3 // 0
0 % 10 // 0
7 % 12 = 7. У Вас 7 телефонов и 12 друзей. Дать каждому хотя бы по одному телефону не получится, ведь тогда кого-то придётся обделить. Поэтому каждый получит 0 телефонов, и все 7 останутся у Вас. Хорошо иметь мало телефонов и много друзей!6 % 3 = 0. 6 телефонов и 3 друга: каждый получит по 2 телефона, и не останется ничего.0 % 10 = 0. Телефонов нет, поэтому каждый из 10 друзей получит 0 телефонов, и остаток тоже равен 0. Это не ошибка: мы не делим на ноль, а делим ноль на 10.
А вот делить на ноль и брать остаток от деления на ноль нельзя: выражения 10 / 0 и 10 % 0 — ошибка в программе.
В общем виде: если взять остаток от деления a на b, получится некоторое число r (от англ. remainder — остаток). Для неотрицательного a и положительного b оно всегда лежит на полуинтервале от 0 включительно до b не включительно:
a % b = r, 0 ≤ r < b
С тем, что r может быть равен 0, мы уже разобрались. Но почему r всегда строго меньше b? Вспомните: b — это количество друзей. Если телефонов осталось столько же, сколько друзей, или больше, значит, Вы раздали не всё. Например, если осталось 7 телефонов, а друзей 5, каждому можно дать ещё по одному, и тогда останется 2.
С отрицательными числами оператор % тоже работает, но знак результата совпадает со знаком делимого: -7 % 3 равно -1. На первых уроках такие случаи нам не понадобятся.
Арифметические операторы на практике
Напишем программу, которая считывает два целых числа и выводит результаты всех арифметических операций:
#include <iostream>
int main() {
int a, b;
std::cin >> a >> b;
std::cout << a + b << std::endl;
std::cout << a - b << std::endl;
std::cout << a * b << std::endl;
std::cout << a / b << std::endl;
std::cout << a % b << std::endl;
return 0;
}
std::endl переводит вывод на новую строку, как и символ '\n', с которым мы познакомились в прошлой статье. Поэтому каждый результат окажется на отдельной строке. Введём числа 5 и 2:
5 2
7
3
10
2
1
Деление здесь целочисленное, поэтому 5 / 2 дало 2, а остаток от деления 5 на 2 равен 1: 5 телефонов, 2 друга, остался 1 телефон.
Теперь заменим тип переменных на double. Для вещественных чисел оператор % запрещён: непонятно, как делить 2,5 телефона на 3,5 друга. Если оставить эту строку, программа не скомпилируется, поэтому закомментируем её:
#include <iostream>
int main() {
double a, b;
std::cin >> a >> b;
std::cout << a + b << std::endl;
std::cout << a - b << std::endl;
std::cout << a * b << std::endl;
std::cout << a / b << std::endl;
// std::cout << a % b << std::endl;
return 0;
}
5 2
7
3
10
2.5
Мы снова ввели 5 и 2 — как будто целые числа. Но переменные имеют тип double, поэтому деление стало вещественным, и результат равен 2.5. Остальные операции работают так же, как с целыми числами.
Сокращённые операторы присваивания
Результат вычислений можно не только выводить на экран, но и сохранять в другую переменную:
int result = a + b * 2;
Порядок действий здесь такой же, как в математике: сначала выполняется умножение, затем сложение. Если a = 5 и b = 2, в result окажется 9.
Часто нужно изменить значение переменной относительно её текущего значения, например прибавить к ней 2 или вычесть 2. Это можно записать полностью или в сокращённом виде:
result = result + 2;
result += 2;
result = result - 2;
result -= 2;
Строки в каждой паре делают одно и то же. Сокращённые версии есть для всех арифметических операторов:
| Сокращённая запись | Полная запись |
|---|---|
a += b | a = a + b |
a -= b | a = a - b |
a *= b | a = a * b |
a /= b | a = a / b |
a %= b | a = a % b |
Сокращённая запись короче и легче читается, поэтому берите её на вооружение.
Инкремент и декремент
Очень часто значение переменной нужно увеличить или уменьшить ровно на единицу. Для этого в C++ есть специальные операторы: инкремент (от англ. increase — увеличивать) и декремент (от англ. decrease — уменьшать). Особенно часто они встречаются в циклах и во всём, что мы будем изучать после них.
| инкремент | декремент | |
|---|---|---|
| постфиксный | a++ | a-- |
| префиксный | ++a | --a |
Таблицу читают так: a++ стоит на пересечении строки «постфиксный» и столбца «инкремент», значит, это постфиксный инкремент. Он называется постфиксным, потому что плюсы стоят после имени переменной. В префиксной форме плюсы или минусы стоят перед именем: ++a и --a.
Любой инкремент — и постфиксный, и префиксный — увеличивает переменную ровно на 1, почти как a += 1 или a = a + 1. Любой декремент уменьшает переменную на 1. Но делают они это по-разному, и разница проявляется, когда инкремент стоит внутри выражения. Это, пожалуй, самая сложная часть урока, но на примере она станет понятной.
Постфиксный инкремент: c++
#include <iostream>
int main() {
int c = 5;
int result = c++ / 2;
std::cout << c << " " << result;
return 0;
}
6 2
Постфиксный инкремент сначала использует текущее значение переменной в выражении и только потом увеличивает её. В момент вычисления c++ / 2 переменная c ещё равна 5, поэтому result получает значение 5 / 2 = 2. После этого c увеличивается и становится равна 6.
Префиксный инкремент: ++c
#include <iostream>
int main() {
int c = 5;
int result = ++c / 2;
std::cout << c << " " << result;
return 0;
}
6 3
Префиксный инкремент сначала увеличивает переменную и только потом использует её новое значение. Переменная c становится равна 6, и уже после этого выполняется деление: result = 6 / 2 = 3.
В обоих примерах c в итоге равна 6, но результаты выражений разные.
С декрементом всё так же, только переменная уменьшается на 1: постфиксный c-- сначала использует значение, а потом уменьшает его, префиксный --c — сначала уменьшает, а потом использует.
Приведение типов
Последняя тема урока — приведение типов, то есть преобразование значения одного типа в другой. Ничего сложного здесь нет, просто это нужно знать. Приведение типов бывает двух видов: неявное и явное.
Неявное приведение типов
Начнём с неявного приведения — на нём проще понять, что вообще такое приведение типов.
#include <iostream>
int main() {
int a = 5;
double b = 2.0;
std::cout << a / b;
return 0;
}
Переменную b мы записали как 2.0, хотя можно было написать просто 2: так сразу видно, что это дробное число, и код читается чище.
В выражении a / b операнды, то есть значения, над которыми выполняется операция, имеют разные типы: int и double. Однако арифметические операции в C++ выполняются над операндами одного типа: int с int, double с double и так далее. Если типы разные и программист не указал, к какому типу их привести, компилятор приведёт их к общему типу сам. Это и есть неявное приведение типов.
Главная идея: меньший тип приводится к большему. Операнд a имеет тип int и занимает 4 байта, а операнд b — тип double и 8 байт. Поэтому a приводится к double. Если бы, наоборот, больший тип приводился к меньшему, часть данных могла бы потеряться.
Важно, что тип самой переменной a при этом не меняется. Только на момент деления её значение 5 используется как 5.0, поэтому и результат получается типа double:
2.5
Правило «меньший к большему» стоит немного уточнить. Компилятор учитывает не только размер типа, но и его вид:
- если один операнд вещественный, а другой целый, целый всегда приводится к вещественному. Например,
long long intиdoubleзанимают по 8 байт, но результат выражения с ними будет иметь типdouble; - если оба операнда вещественные, меньший тип приводится к большему, например
floatкdouble; - если оба операнда целые, меньший тип приводится к большему, например
intкlong long int. При этомchar,short intиboolперед вычислением всегда превращаются как минимум вint.
Смешивать в одном выражении знаковые и беззнаковые типы не стоит: правила для них сложнее и могут привести к неожиданным результатам.
Но даже когда правила известны, неявные преобразования делают код менее понятным. В программировании есть принцип: явное лучше неявного. Поэтому старайтесь не полагаться на неявное приведение типов и указывайте преобразование явно.
Явное приведение типов: C-style cast
При явном приведении программист сам говорит компилятору, к какому типу нужно привести значение. Такое приведение можно записать в двух стилях.
Первый стиль — C-style cast, приведение в стиле языка C. Он перешёл в C++ для обратной совместимости, чтобы код, написанный на C, работал и в C++:
#include <iostream>
int main() {
double value = 2.5;
std::cout << (int)(value);
return 0;
}
2
Тип, к которому приводим, записывают в круглых скобках перед значением. Запись (int)(value) означает: «используй value как int». При приведении вещественного числа к целому дробная часть отбрасывается, а не округляется, поэтому на экране мы увидим 2.
Современное приведение: static_cast
Второй стиль — C++-style cast. В современном C++ для таких преобразований используют static_cast:
#include <iostream>
int main() {
double value = 2.5;
std::cout << static_cast<int>(value);
return 0;
}
2
В угловых скобках указывают тип, к которому нужно привести значение, а в круглых — само значение. Результат такой же, как у C-style cast.
Почему же лучше использовать static_cast? C-style cast почти ничего не проверяет и может незаметно выполнить опасное преобразование. static_cast строже: если такое преобразование недопустимо, компилятор сообщит об ошибке. Кроме того, static_cast легко заметить и найти в коде.
Сейчас разница может казаться несущественной, но она станет важной, когда мы дойдём до указателей, наследования и полиморфизма. Поэтому привыкайте к static_cast с самого начала, даже если он выглядит более громоздко.
Частая ошибка при использовании static_cast
Пусть у нас есть две целые переменные, и мы хотим разделить их так, чтобы получить дробный результат. Многие новички пишут так:
#include <iostream>
int main() {
int a = 5, b = 2;
std::cout << static_cast<double>(a / b);
return 0;
}
2
Почему не 2.5? Сначала вычисляется выражение в круглых скобках. a / b — это деление int на int, поэтому дробная часть отбрасывается уже на этом шаге, и получается 2. Затем число 2 приводится к double, но это по-прежнему 2.
Правильно приводить к double сами операнды, а не результат деления:
#include <iostream>
int main() {
int a = 5, b = 2;
std::cout << static_cast<double>(a) / static_cast<double>(b);
return 0;
}
2.5
Теперь на момент вычисления выражения a и b интерпретируются как double, и деление получается вещественным. Строго говоря, достаточно привести и один операнд — второй приведётся к double неявно. Но явное приведение обоих операндов делает код понятнее.
Подведём итоги
В этой статье Вы познакомились с переменными, типами данных и их модификаторами, научились вводить данные с клавиатуры, выполнять арифметические операции и приводить типы.
Главное, что нужно запомнить:
- переменная имеет имя и тип и хранит значение;
- в C++ четыре примитивных типа данных: целочисленный, вещественный, символьный и логический;
- модификаторы одного типа различаются диапазоном значений и объёмом занимаемой памяти;
- в большинстве задач достаточно модификаторов
int,double,charиbool; std::cinсчитывает данные с клавиатуры и пропускает лишние пробелы и переводы строк;- деление
intнаintотбрасывает дробную часть, а оператор%возвращает остаток от деления; c++сначала использует значение переменной, а потом увеличивает его,++c— наоборот;- при неявном приведении меньший тип приводится к большему, а для явного приведения используйте
static_cast.
Теория без практики быстро забывается, поэтому обязательно закрепите материал. Авторизуйтесь на сайте, отметьте статью как прочитанную и перейдите во вкладку «Решать задачи». Отправляйте решения на проверку, разбирайте результаты тестов и отслеживайте свой прогресс.
Использованная литература
- Рабочий проект стандарта C++ [Электронный ресурс]. — URL: https://eel.is/c++draft/ (дата обращения: 01.10.2026).
- Рабочий проект стандарта C++26 — N5054 [Электронный ресурс]. — URL: https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2026/n5054.pdf (дата обращения: 01.10.2026).
- ISO/IEC 14882:2024 — язык программирования C++ [Электронный ресурс]. — URL: https://www.iso.org/standard/83626.html (дата обращения: 01.10.2026).
- Фундаментальные типы данных [Электронный ресурс]. — URL: https://en.cppreference.com/w/cpp/language/types (дата обращения: 01.10.2026).
- Типы целых чисел фиксированной ширины [Электронный ресурс]. — URL: https://en.cppreference.com/w/cpp/types/integer (дата обращения: 01.10.2026).
- Оператор sizeof [Электронный ресурс]. — URL: https://en.cppreference.com/w/cpp/language/sizeof (дата обращения: 01.10.2026).
- std::numeric_limits [Электронный ресурс]. — URL: https://en.cppreference.com/w/cpp/types/numeric_limits (дата обращения: 01.10.2026).
- Инициализация переменных [Электронный ресурс]. — URL: https://en.cppreference.com/w/cpp/language/initialization (дата обращения: 01.10.2026).
- Стандартный поток ввода std::cin [Электронный ресурс]. — URL: https://en.cppreference.com/w/cpp/io/cin (дата обращения: 01.10.2026).
- Арифметические операторы [Электронный ресурс]. — URL: https://en.cppreference.com/w/cpp/language/operator_arithmetic (дата обращения: 01.10.2026).
- Операторы присваивания, включая сокращённые [Электронный ресурс]. — URL: https://en.cppreference.com/w/cpp/language/operator_assignment (дата обращения: 01.10.2026).
- Операторы инкремента и декремента [Электронный ресурс]. — URL: https://en.cppreference.com/w/cpp/language/operator_incdec (дата обращения: 01.10.2026).
- Функция std::fmod [Электронный ресурс]. — URL: https://en.cppreference.com/w/cpp/numeric/math/fmod (дата обращения: 01.10.2026).
- Неявные преобразования типов [Электронный ресурс]. — URL: https://en.cppreference.com/w/cpp/language/implicit_conversion (дата обращения: 01.10.2026).
- Оператор static_cast [Электронный ресурс]. — URL: https://en.cppreference.com/w/cpp/language/static_cast (дата обращения: 01.10.2026).
- Явное приведение типов в стиле C [Электронный ресурс]. — URL: https://en.cppreference.com/w/cpp/language/explicit_cast (дата обращения: 01.10.2026).
- Programming: Principles and Practice Using C++, 3-е издание [Электронный ресурс]. — URL: https://www.stroustrup.com/PPP3.html (дата обращения: 01.10.2026).
- A Tour of C++, 3-е издание [Электронный ресурс]. — URL: https://www.stroustrup.com/tour3.html (дата обращения: 01.10.2026).
- Скотт Майерс — Эффективный и современный C++ [Электронный ресурс]. — URL: https://www.oreilly.com/library/view/effective-modern-c/9781491908419/ (дата обращения: 01.10.2026).