Общаться с 64-битным long long в Visual C++
Тип данных long long — это единственный целочисленный тип данных в Visual С++, который весит 8 байт и может использовать значения выше 2^31 — 1. -2^31. Если у вас 64-битная система, то тип long тоже будет 8-байтовым. А в 32-разрядной системе приходится довольствоваться только им. Этот тип данных получил права только в последних версиях С++, например, в С++ 6.0 вы его найдёте, но работать с ним там одно удовольствие.
Несмотря на то, что у long long есть закреплённые права и обязанности. Использовать его всё равно следует по-особенному. Для начала следует учесть, что простое присваивание ему значений не прокатит. Если вы хотите присвоить значение переменной типа long long, то не забудьте приставить к числу две буквы LL (которые видимо символизируют, что число «longlong» 64-битное).
long long temp = 1100LL;
Далее: про соответствие типов. Думаю, когда человек первый раз «берёт в руки» этот тип данных ему становится интересно, а как он соотносится с типом int?
Отношения у long long c int очень интересные. Если вы хотите складывать два числа int, зная, что результат выйдет за диапазон int, то вы можете использовать long long. Сложите два int присвоите long long и все будут счастливы.
int a1 = 2000000000;
int a2 = 2000000000;
long long b = a1 + a2;
b равен 400000000. И здесь проблем возникнуть не должно.
С умножением int’ов дела гораздо интереснее. Если вы просто возьмёте две переменные типа int и перемножите их между собой, зная, что результат выйдет за пределы int, то ничего у вас не получится. Переменная типа long long будет просто равна нулю. В итоге, чтобы умножать в Visual C++ числа типа int и получить long long следует проделать сложения через цикл, т.е. если мы хотим умножить число а на число b, то всё будет выглядеть так.
long long temp = 0;
for (int i = 0; i < a; i++) temp += b;
>
И только тогда захочет быть произведением.
Контакты
Петрозаводск, пр. Ленина, 31 (IT-парк ПетрГУ), кабинет 211.
Замечания по содержимому и работе сайта принимаются по адресу webmaster@acm.petrsu.ru.
Последние обновления
11.10.2023
8.07.2023
9.04.2023
27.11.2022
13.10.2022
15.11.2021
3.03.2021
27.01.2021
14.11.2020
3.09.2020
Базовые типы данных в C++
| Вид типа данных | Название | Размер | Диапазон | Комментарий |
|---|---|---|---|---|
| Логический | bool | 8 бит | true / false | Значение false тождественно равно нулю. Значение true — это любое другое значение, кроме нуля. |
| Символьный | char | 8 бит | 0 – 255 | Несмотря на то, что тип символьный, char хранит число — код символа в таблице ASCII. |
| Целочисленный | int | 32 бита | Примерно -2⋅10 9 – 2⋅10 9 |
Диапазон обусловлен количеством бит, которые кодируют значение числа. В данном случае их ровно 31, так как один бит остаётся на кодирование знака. Так как биты кодируют значение бинарным кодом, диапазон на самом деле такой: -2 31 – 2 31 — 1. |
| unsigned int | 32 бита | 0 – 4⋅10 9 | Модификатор целочисленных типов unsigned делает тип данных беззнаковым, то есть теперь под кодирование значения числа используются все 32 бита. Соответственно, диапазон теперь не включает отрицательный чисел, зато сверху ограничен более высоким значением 2 32 — 1. | |
| long long | 64 бита | Примерно -9⋅10 18 – 9⋅10 18 |
-2 63 – 2 63 — 1 | |
| unsigned long long | 64 бита | 0 – 18⋅10 18 | 0 – 2 64 — 1 | |
| Вещественный (с плавающей запятой) | double | 64 бита | 1,7E +/- 308 (15 знаков) |
Этот тип данных хранит вещественные числа с точностью до 308 знаков после (или до) запятой, но не более 15 цифр после самой левой значащей цифры (не нуля). |
Типы данных в языке C++
Для вывода на консоль символов wchar_t следует использовать не std::cout, а поток std::wcout. При этом поток std::wcout может работать как с char, так и с wchar_t. А поток std::cout для переменной wchar_t вместо символа будет выводить его числовой код.
В стандарте С++11 были добавлены типы char16_t и char32_t, которые ориентированы на использование Unicode. Однако на уровне ОС пока не реализованы потоки для работы с этими типами. Поэтому если потребуется вывести на консоль значения переменных этих типов, то необходимо преобразовать переменные к типам char или wchar_t.
Стандарт устанавливает лишь минимальные значения размера, который тип занимает в памяти. Например, для типов int и short минимальное значение — 16 бит, для типа long — 32 бита. При этом размер типа long должен быть не меньше размера типа int, а размер типа int — не меньше размера типа short, а размер типа long double должен быть больше double. Для определения точного размера определенного типа в С++ есть оператор sizeof(), который возвращает размер памяти в байтах, которую занимает переменная.
Преобразование типов
Преобразование типов происхоит когда требуется конвертация значений из одного типа данных в другой. Преобразование типов может происходить в следующих случаях:
- присваивание или инициализация переменной
- передача значений в функцию
- возврат значения из функции
- вычисление арифметических выражений
Перобразование может быть неявным, когда компилятор автоматически конвертирует один тип в другой, и явным, когда программист сам указывает в какой тип нужно сконвертировать значение.
Неявное преобразование типов
Преобразование, когда значение из одного типа конвертируется в другой тип, большего размера, называется числовым расширением и всегда безопасно и не приводит к потере данных.
Когда значение типа данных конвертируется в аналогичный, но меньший по размеру тип данных, или конвертация происходит между разными типами данных, то это называется числовой конверсией и может (но не всегда) приводить к потере данных.
Арифметические выражения
При обработке выражений компилятор разбивает каждое выражение на отдельные подвыражения. Арифметические операторы требуют, чтобы их операнды были одного типа данных. Чтобы это гарантировать, компилятор использует следующие правила:
- если операндом является целое число меньше (по размеру/диапазону) типа int, то оно подвергается интегральному расширению в int или в unsigned int
- если операнды разных типов данных, то компилятор вычисляет операнд с наивысшим приоритетом и неявно конвертирует тип другого операнда в такой же тип, как у первого
Приоритет типов операндов для арифметических операций:
- long double (самый высокий);
- double
- float
- unsigned long long
- long long
- unsigned long
- long
- unsigned int
- int (самый низкий)
Явное преобразование типов
В языке C++ есть 5 видов операций явного преобразования типов:
- конвертация в стиле языка C
- использование операции static_cast
- использование операции dynamic_cast
- использование операции const_cast
- использование операции reinterpret_cast
Конвертация в стиле языка C
В программировании на языке Cи явное преобразование типов данных выполняется с помощью оператора ( ).
int i1 = 11; int i2 = 3; float x = (float)i1 / i2;
Язык C++ также позволяет использовать этот оператор следующим образом:
int i1 = 11; int i2 = 3; float x = float(i1) / i2;
Конвертация в стиле языка C не проверяется компилятором, поэтому она может быть неправильно использована, например привести к переполнению.
Операция static_cast
static_castтип>(выражение)
Операция static_cast в языке C++ осуществляет явное и допустимое приведение заданного выражения в указанный тип данных. Операция static_cast аналогична операции «круглые скобки» с одним исключением: она не выполняет приведение указателей на неродственные типы (для этого применяется операция reinterpret_cast). Основным преимуществом операции static_cast является проверка выполнения компилятором во время компиляции с генерацией предупреждений, что усложняет возможность возникновения ошибок.
int i1 = 11; int i2 = 3; float x = static_cast(i1) / i2;
Операция dynamic_cast
dynamic_castтип>(указатель/ссылка)
В языке C++ операция dynamic_cast используется для конвертации указателей родительского класса в указатели дочернего класса. Это является наиболее распространенным применением оператора dynamic_cast.Этот процесс называется приведением к дочернему типу (или «понижающим приведением типа»).
Если dynamic_cast не может выполнить конвертацию, то он возвращает нулевой указатель, поэтому нужно делать проверку результата динамического приведения на нулевой указатель. Оператор dynamic_cast также может использоваться и со ссылками. Работа dynamic_cast со ссылками аналогична работе с указателями, но поскольку в языке C++ не существует «нулевой ссылки», то dynamic_cast не может возвратить «нулевую ссылку» при сбое. Вместо этого dynamic_cast генерирует исключение типа std::bad_cast.
Поскольку динамическое приведение выполняет проверку во время работы программы, использование оператора dynamic_cast немного снижает производительность. Понижающее приведение также может быть выполнено и через оператор static_cast. Основное отличие заключается в том, что static_cast не выполняет проверку во время запуска программы. Это позволяет оператору static_cast быть быстрее, но опаснее оператора dynamic_cast.
Существуют случаи, в которых понижающее приведение с использованием оператора dynamic_cast не работает:
- Наследование типа private или типа protected
- Классы, которые не объявляют или не наследуют классы с какими-либо виртуальными функциями (и, следовательно, не имеют виртуальных таблиц)
- Случаи, связанные с виртуальными базовыми классами
В общем, использование виртуальных функций должно быть предпочтительнее использования понижающего приведения. Однако в следующих случаях понижающее приведение является лучшим вариантом:
- Если нет возможности изменить родительский класс, чтобы добавить в него свою виртуальную функцию (например, если родительский класс является частью Стандартной библиотеки С++)
- Если требуется доступ к чему-либо, что есть только в дочернем классе (например к функции, которая существует только в дочернем классе)
- Если добавление виртуальной функции в родительский класс не имеет смысла. В таком случае, в качестве альтернативы, если не нужно создавать объект родительского класса, можно использовать чистую виртуальную функцию
Parent *p = getParentObject(); Child *ch = dynamic_cast(p);
Операция const_cast
const_castтип>(указатель/ссылка)
Операция const_cast используется для снятия/установки модификаторов const, volatile, mutable. Часто это применяется, чтобы обойти неудачную архитектуру программы или библиотеки, для стыковки С и С++, для передачи информации через обобщённые указатели void*, для одновременного написания const- и не-const-версии функции.
Операцию const_cast для сброса const следует использовать только в крайних случаях, когда точно известно поведение программы. А вот использование операции const_cast ради добавления константности можно использовать без опасений получить неопределенное поведение.
При использовании операции const_cast выражение должно быть ссылкой или указателем, а тип должен совпадать с исходным типом вплоть до модификаторов const, volatile и mutable.
void foo(char*)<> int main() < char const* str = ""; foo(const_cast(str)); //снятие константности позволяет использовать функцию >
Операция reinterpret_cast
reinterpret_castтип>(выражение)
Оператор приведения reinterpret_cast используется для приведения несовместимых типов с сохранением битового представления. Может приводить целое число к указателю, указатель к целому числу, указатель к указателю (это же касается и ссылок).
Является функционально усеченным аналогом приведения типов в стиле языка С.
Отличие состоит в том, что reinterpret_cast не может снимать квалификаторы const и volatile, а также не может делать небезопасное приведение типов не через указатели, а напрямую по значению. Например, переменную типа int к переменной типа double привести при помощи reinterpret_cast нельзя.
Ограничения на тип>:
- Если выражение> — это значение порядкового типа или указатель, то тип> может быть порядковым типом или указателем
- Если выражение> — это ссылка, то тип> должен быть ссылкой
int i; char *p = "Это строка"; i = reinterpret_cast(p); // Приведение указателя к целому числу
Псевдонимы типов
Ключевое слово typedef позволяет программисту создать псевдоним для любого типа данных и использовать его вместо фактического имени типа.
typedef тип> псевдоним>;
Обычно к псевдонимам typedef добавляют окончание _t.
Псевдонимы используются для улучшения документации и читаемости кода. Например чтобы понимать смысл возвращаемой функцией значения, а не просто int или long.
Псевдонимы также позволяют изменить базовый тип объекта без внесения изменений в большое количество кода. Например, если для хранения какого-либо номера использовался тип short, а теперь нужно его увеличить до int, то псевдонимы позволят это сделать без необходимости поиска и замены типов во всем коде, да еще и с анализом нужных переменных.
Еще одним большим преимуществом typedef является возможность скрывать специфические для определенных платформ детали. Поскольку char, short, int и long не указывают свой размер, то для кроссплатформенных программ довольно часто используется typedef для определения псевдонимов, которые включают размер типа данных в битах. Например, int8_t — это 8-битный signed int, int16_t — это 16-битный signed int, а int32_t — это 32-битный signed int.
Еще одно использование псевдонимов — это упрощение сложных типов. Например вместо указания типа std::vector>, можно задать для него псевдоним pairlist_t и в дальнейшем использовать уже его.
Новый синтаксис
Из-за неочевидного порядка указания типа и псевдонима в typedef, в стандарте С++11 ввели новый улучшенный синтаксис, который имитирует способ объявления переменных. Этот синтаксис называется type alias.
using псевдоним> = тип>;
Следует обратить внимание, что хотя и используется ключевое слово using, оно не имеет ничего общего с using-стейтментами.
Новый синтаксис создания псевдонимов создает меньше проблем при использовании в сложных ситуациях, и его рекомендуется применять вместо обычного typedef, если компилятор поддерживает C++11.
- Уголок в Вконтакте
- Уголок в Телеграм
- Уголок в YouTube
Типы данных в языке C++
В этой статье мы с вами познакомимся с основными типами данных, которые предоставляет разработчику язык C++.
В языке C++ типы данных предназначены для объявления переменных. Тип данных определяет тип и размер данных, которые связаны с какой-то конкретной переменной. Все типы данных делятся на три основные категории: целочисленные, с плавающей точкой и void. Все типы данных задаются стандартом языка C++ и встроены в компилятор.
Посмотрим сразу на список примитивных встроенных в C++ типов данных:
- int — тип данных для хранения целых чисел. Размер памяти от 2 до 4 байт
- float — тип данных для хранения чисел с «плавающей точкой», т.е. десятичных чисел (десятичных дробей). Размер памяти 4 байта
- double — тип данных для хранения чисел с «плавающей точкой» двойной точности. Похож на тип данных float, но обеспечивает большую точность при расчётах и использовании десятичных дробей. Размер памяти 8 байт
- char — тип данных для хранения одного символа. Размер памяти 1 байт
- wchar_t — тип данных для хранения одного «расширенного» символа. Размер памяти 2 байта
- bool — логический тип данных, используется для хранения значений булева типа, т.е. тех, что могут принимать значения «истина» или «ложь». Размер памяти 1 байт
- void — тип данных, обозначающий «пустоту», или «отсутствие какого-то значения». Размер памяти 0 байт
Язык C++ чрезвычайно мощный, и на перечисленном выше списке возможные типы данных, которые могут использоваться в C++, не ограничиваются.
Некоторые из базовых типов могут быть также изменены с помощью одного или нескольких модификаторов типа:
- signed — модификатор типа указывает, что тип является типом со знаком, т.е. может хранить как положительные, так и отрицательные значения.
- unsigned — модификатор типа указывает, что тип является беззнаковым, т.е. хранит только неотрицательные значения.
- short — применяется для хранения небольших числовых значений. Когда применяется к типу данных int, то урезает диапазон хранимых значений для типа int, а также размер памяти под хранимое значение с 4 байт до 2 байт
- long — применяется для хранения больших числовых значений. Когда применяется к типу данных int, то расширит диапазон хранимых значений для типа int, а также размер памяти под хранимое значение с 4 байт до 8 байт
Далее по тексту статьи мы узнаем, как это делается, а пока остановимся на тех примитивных типах данных, что описаны выше, и посмотрим на несколько примеров их использования.
Тип данных int
Тип данных int широко применяется там, где нужна работа с целыми числами. По умолчанию можно хранить в переменных типа int как положительные, так и отрицательные значения. Ниже показано простое объявление трёх разных переменных с типом int:
int daysInWeek = 7; int monthsInYear = 12; int currentYear = 2022; int someNegativeValue = -20;
Тип данных float
Тип данных float, в отличие от типа int, используется для хранения чисел с плавающей точкой, или, проще говоря, десятичных дробей. Поддерживает как положительные, так и отрицательные значения. Давайте взглянем, как можно объявить переменные типа float:
float bottleVolume = 0.5; float normalHumanTemperature = 36.6; float someNegativeFloatValue = -72.7;
Тип данных double
Как уже было сказано, тип данных double очень похож на float, за исключением того, что он использует больше памяти для хранения данных (8 байт вместо 4 байт для типа float). В остальном определения переменных с типом double выглядят почти так же, как и для вышеупомянутых типов:
double doubleVal1 = 1234.56789101112; double doubleVal2 = -77.1122334455667788;
Тип данных char
Тип данных char используется для хранения символов (от англ. characters, отсюда и название самого типа данных). Значения для этого типа данных заключаются с обеих сторон в одинарные кавычки ( ‘ ), например, давайте объявим пару переменных с этим типом данных:
char chA = 'A'; char chB = 'B'; char chQuestionMark = '?';
Тип данных bool
Тип данных bool может иметь всего одно из двух возможных значений — true или false. Значение true обозначает «истину», а false — «ложь». Посмотрим на пример:
bool isNegative = false; bool isDataEntered = true;
В примере выше объявили две переменных с типом bool. Одна из них isNegative инициализирована значением false и может, например, обозначать примерно такой смысл: «не является отрицательным» (если мы пишем какую-то логику, проверяющую числа на отрицательное значение). Вторая переменная isDataEntered инициализирована значением true (помним, что это значение соответствует «истине») и может, к примеру, обозначать что «данные введены». Если мы ей где-нибудь дальше в тексте присвоили бы значение false, это могло бы для нас означать «данные не введены».
Тип данных void
Тип данных void представляет отсутствие данных. Он обозначает буквально «ничто» или «нет значения». Тип данных void как правило используется при работе с функциями и указателями. Важным моментом, который нужно запомнить, является то, что мы не можем объявить переменную с типом void. (к слову, указатели с использованием типа void вполне допустимы в C++).
А вот как может выглядеть объявление функции с типом void, которая «ничего не возвращает» (фактически, этот метод является процедурой):
void functionReturningNothing() < // делаем здесь что-то. >
Теперь, когда мы рассмотрели основные встроенные примитивные типы данных, вернёмся к вопросу о том, как они могут быть изменены с помощью модификаторов типа на примере следующей таблицы:
| Тип данных | Размер памяти под тип данных | Диапазон принимаемых значений |
| int | 4 байта | от -2 147 483 648 до 2 147 483 647 |
| unsigned int | 4 байта | от 0 до 4 294 967 295 |
| signed int | 4 байта | от -2 147 483 648 до 2 147 483 647 |
| short int | 2 байта | от -32 768 до 32 767 |
| unsigned short int | 2 байта | от 0 до 65 535 |
| char | 1 байт | от -127 до 127 или от 0 до 255 |
| unsigned char | 1 байт | от 0 до 255 |
| signed char | 1 байт | от -127 до 127 |
| long int | 8 байт | от -2 147 483 648 до 2 147 483 647 |
| signed long int | 8 байт | от -2 147 483 648 до 2 147 483 647 |
| unsigned long int | 8 байт | от 0 до 4 294 967 295 |
| long long int | 8 байт | от -(2^63) до (2^63)-1 |
| unsigned long long int | 8 байт | от 0 до 18 446 744 073 709 551 615 |
| float | 4 байта | относительное значение диапазона: является наименьшим типом с плавающей запятой в C++ (абсолютный размер встроенных типов с плавающей запятой не указан в стандарте языка C++) |
| double | 8 байт | относительное значение диапазона: значения больше или равны типу float, но меньше, чем у long double (абсолютный размер встроенных типов с плавающей запятой не указан в стандарте языка C++) |
| long double | 12 байт | относительное значение диапазона принимаемых значений: значения больше или равны размеру типа double (абсолютный размер встроенных типов с плавающей запятой не указан в стандарте языка C++) |
Посмотрим, как можно объявлять переменные с некоторыми из расширенных типов данных, представленных в таблице:
unsigned char ch = 255; unsigned long int veryLongInt = 18446744073709551615; long double veryLongDoubleValue = 0.333333333333333333333333333333333333333333333333333;
Напоследок, мы можем написать небольшую программу на C++, которая выведет для нас размер выделяемой памяти под все описанные в статье типы данных C++. Обратите внимание на использования для этой цели специального оператора sizeof:
#include int main()
Результат выполнения программы на экране консоли будет выглядеть следующим образом:
Size of 'int' data type: 4 bytes
Size of 'unsigned int' data type: 4 bytes
Size of 'signed int' data type: 4 bytes
Size of 'short int' data type: 2 bytes
Size of 'unsigned short int' data type: 2 bytes
Size of 'float' data type: 4 bytes
Size of 'double' data type: 8 bytes
Size of 'long double' data type: 8 bytes
Size of 'bool' data type: 1 bytes
Size of 'char' data type: 1 bytes
Size of 'unsigned char' data type: 1 bytes
Size of 'signed char' data type: 1 bytes
Size of 'wchar_t' data type: 2 bytes
Size of 'long int' data type: 4 bytes
Size of 'unsigned long int' data type: 4 bytes
Size of 'long long int' data type: 8 bytes
Size of 'unsigned long long int' data type: 8 bytes
Как видите, даже если Вы вдруг забыли, сколько именно байт в памяти будет занимать переменная какого-то типа данных. Ведь при помощи указанного выше способа можно легко это определить самостоятельно, средствами языка C++.
Надеюсь, эта статья пригодится и будет полезна всем разработчикам, работающим с языком C++.
Напоследок отмечу, что если Вы работаете со средой C++ в среде разработки Microsoft Visual Studio, то более подробно о типах данных в языке C++ можно также почитать здесь.
Спасибо за внимание, удачи! Буду благодарен за отзывы в комментариях к этой статье.