Какие основные типы данных используются в C++?
Тип данных – это множество допустимых значений, которые может принимать тот или иной объект, а также множество допустимых операций, которые применимы к нему. В современном понимании тип также зависит от внутреннего представления информации.
Таким образом, данные различных типов хранятся и обрабатываются по-разному. Тип данных определяет:
- внутреннее представление данных в памяти компьютера;
- объем памяти, выделяемый под данные;
- множество (диапазон) значений, которые могут принимать величины этого типа;
- операции и функции, которые можно применять к данным этого типа.
Исходя из данных характеристик, необходимо определять тип каждой величины, используемой в программе для представления объектов. Обязательное описание типа позволяет компилятору производить проверку допустимости различных конструкций программы. От выбора типа величины зависит последовательность машинных команд, построенная компилятором.
В C++ существует семь базовых, или основных, типов данных (точнее, семь базовых идентификаторов типа).
Идентификатор типа | Тип данных |
bool | Логический тип |
char | Символьный тип |
wchar_t | Символьный двухбайтовый тип |
double | Действительные числа двойной точности |
fl oat | Действительные числа |
int | Целые числа |
void | Значение не возвращается |
Логический тип
Логический тип bool может хранить одно из двух значений: true (истинно, верно) и false (неверно, ложно). Например, определим пару переменных данного типа и выведем их значения на консоль:
1 2 3 4 5 6 7 8 9 |
#include <iostream>
int main() |
При выводе значения типа bool преобразуются в 1 (если true) и 0 (если false). Как правило, данный тип применяется преимущество в условных выражениях.
Значение по умолчанию для переменных этого типа — false.
Целочисленные типы
Целые числа в языке C++ представлены следующими типами:
- signed char: представляет один символ. Занимает в памяти 1 байт (8 бит). Может хранить любое значение из диапазона от -128 до 127
- unsigned char: представляет один символ. Занимает в памяти 1 байт (8 бит). Может хранить любое значение из диапазона от 0 до 255
- char: представляет один символ в кодировке ASCII. Занимает в памяти 1 байт (8 бит). Может хранить любое значение из диапазона от -128 до 127, либо от 0 до 255
Несмотря на то, что данный тип представляет тот же диапазон значений, что и вышеописанный тип signed char, они не эквивалентны. Тип char предназначен для хранения числового кода символа и в реальности может представлять как signed byte, так и unsigned byte в зависимости от конкретного компилятора.
- short: представляет целое число в диапазоне от –32768 до 32767. Занимает в памяти 2 байта (16 бит).
Данный тип также имеет псевдонимы short int, signed short int, signed short.
- unsigned short: представляет целое число в диапазоне от 0 до 65535. Занимает в памяти 2 байта (16 бит).
Данный тип также имеет синоним unsigned short int.
- int: представляет целое число. В зависимости от архитектуры процессора может занимать 2 байта (16 бит) или 4 байта (32 бита). Диапазон предельных значений соответственно также может варьироваться от –32768 до 32767 (при 2 байтах) или от −2 147 483 648 до 2 147 483 647 (при 4 байтах). Но в любом случае размер должен быть больше или равен размеру типа short и меньше или равен размеру типа long
Данный тип имеет псевдонимы signed int и signed.
- unsigned int: представляет положительное целое число. В зависимости от архитектуры процессора может занимать 2 байта (16 бит) или 4 байта (32 бита), и из-за этого диапазон предельных значений может меняться: от 0 до 65535 (для 2 байт), либо от 0 до 4 294 967 295 (для 4 байт).
Имеет псевдоним unsigned
- long: в зависимости от архитектуры может занимать 4 или 8 байт и представляет целое число в диапазоне от −2 147 483 648 до 2 147 483 647 (при 4 байтах) или от −9 223 372 036 854 775 808 до +9 223 372 036 854 775 807 (при 8 байтах). Занимает в памяти 4 байта (32 бита) или.
Имеет псевдонимы long int, signed long int и signed long
- unsigned long: представляет целое число в диапазоне от 0 до 4 294 967 295. Занимает в памяти 4 байта (32 бита).
Имеет синоним unsigned long int.
- long long: представляет целое число в диапазоне от −9 223 372 036 854 775 808 до +9 223 372 036 854 775 807. Занимает в памяти 8 байт (64 бита).
Имеет псевдонимы long long int, signed long long int и signed long long.
- unsigned long long: представляет целое число в диапазоне от 0 до 18 446 744 073 709 551 615. Занимает в памяти, как правило, 8 байт (64 бита).
Имеет псевдоним unsigned long long int.
Для представления чисел в С++ применятся целочисленные литералы со знаком или без, типа -10 или 10. Например, определим ряд переменных целочисленных типов и выведем их значения на консоль:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
#include <iostream>
int main() |
Но стоит отметить, что все целочисленные литералы по умолчанию представляют тип int. Так, выше переменным разных типов присваивались различные числа — 64, -64, 88, -88, 1024 и т.д. Но все эти целочисленные литералы представляют тип int.
Однако мы можем использовать целочисленные литералы и других типов. Целочисленные литералы без знака (которые представляют unsigned-типы) имеют суффикс u или U. Литералы типов long и long long имеют суффиксы L/l и LL/ll соответственно:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
#include <iostream>
int main() |
Тем не менее использовать суффиксы необязательно, поскольку, как правило, компилятор может успешно преобразовать целочисленный литерал типа (который технически представляет тип int) к нужному типу без потери информации.
Если число большое, то при вводе мы можем где-то ошибиться. Чтобы упростить читабельность чисел, начиная со стандарта C++14 в язык была добавлена возможность разделения разрядов числа с помощью одинарной кавычки ‘
1 2 3 4 5 6 7 |
#include <iostream>
int main() |
Различные системы исчисления
По умолчанию все стандартные целочисленные литералы представляют числа в привычной нам десятичной системе. Однако C++ также позволяет использовать и числа в других системах исчисления.
Чтобы указать, что число — шестнадцатеричное, перед числом указывается префикс 0x или 0X. Например:
1 2 3 |
int num1{ 0x1A}; // 26 — в десятичной int num2{ 0xFF }; // 255 — в десятичной int num3{ 0xFFFFFF }; //16777215 — в десятичной |
Чтобы указать, что число — восьмеричное, перед числом указывается ноль 0. Например:
1 2 |
int num1{ 034}; // 26 — в десятичной int num2{ 0377 }; // 255 — в десятичной |
Бинарные литералы предваряются префиксом 0b или 0B:
1 2 |
int num1{ 0b11010}; // 26 — в десятичной int num2{ 0b11111111 }; // 255 — в десятичной |
Все эти типы литералов также поддерживают суффиксы U/L/LL:
1 2 3 |
unsigned int num1{ 0b11010U}; // 26 — в десятичной long num2{ 0377L }; // 255 — в десятичной unsigned long num3{ 0xFFFFFFULL }; //16777215 — в десятичной |
Числа с плавающей точкой
Для хранения дробных чисел в C++ применяются числа с плавающей точкой. Число с плавающей точкой состоит из двух частей: мантиссы и показателя степени. Оба могут быть как положительными, так и отрицательными. Величина числа – это мантисса, умноженная на десять в степени экспоненты.
Например, число 365 может быть записано в виде числа с плавающей точкой следующим образом:
3.650000E02 |
В качестве разделителя целой и дробной частей используется символ точки. Мантисса здесь имеет семь десятичных цифр — 3.650000, показатель степени — две цифры 02. Буква E означает экспоненту, после нее указывается показатель степени (степени десяти), на которую умножается часть 3.650000 (мантисса), чтобы получить требуемое значение. То есть, чтобы вернуться к обычному десятичному представлению, нужно выполнить следующую операцию:
3.650000 × 10² = 365 |
Другой пример — возьмем небольшое число:
-3.650000E-03 |
В данном случае мы имеем дело с числом –3.65 × 10-3, что равно –0.00365. Здесь мы видим, что в зависимости от значения показателя степени десятичная точка «плавает». Собственно поэтому их и называют числами с плавающей точкой.
Однако хотя такая запись позволяет определить очень большой диапазон чисел, не все эти числа могут быть представлены с полной точностью; числа с плавающей запятой в целом являются приблизительными представления точного числа. Например, число 1254311179 выглядело бы так: 1.254311E09. Однако если перейти к десятичной записи, то это будет 1254311000. А это не то же самое, что и 1254311179, поскольку мы потеряли три младших разряда.
В языке C++ есть три типа для представления чисел с плавающей точкой:
- float: представляет вещественное число одинарной точности с плавающей точкой в диапазоне +/- 3.4E-38 до 3.4E+38. В памяти занимает 4 байта (32 бита)
- double: представляет вещественное число двойной точности с плавающей точкой в диапазоне +/- 1.7E-308 до 1.7E+308. В памяти занимает 8 байт (64 бита)
- long double: представляет вещественное число двойной точности с плавающей точкой не менее 8 байт (64 бит). В зависимости от размера занимаемой памяти может отличаться диапазон допустимых значений.
В своем внутреннем бинарном представлении каждое число с плавающей запятой состоит из одного бита знака, за которым следует фиксированное количество битов для показателя степени и набор битов для хранения мантиссы. В числах float 1 бит предназначен для хранения знака, 8 бит для экспоненты и 23 для мантиссы, что в сумме дает 32 бита. Мантисса позволяет определить точность числа в виде 7 десятичных знаков.
В числах double: 1 знаковый бит, 11 бит для экспоненты и 52 бит для мантиссы, то есть в сумме 64 бита. 52-разрядная мантисса позволяет определить точность до 16 десятичных знаков.
Для типа long double расклад зависит от конкретного компилятора и реализации этого типа данных. Большинство компиляторов предоставляют точность до 18 — 19 десятичных знаков (64-битная мантисса), в других же (как например, в Microsoft Visual C++) long double аналогичен типу double.
В C++ литералы чисел с плавающими точками представлены дробными числами, которые в качестве разделителя целой и дробной частей применяют точку:
1 | double num {10.45}; |
Даже если переменной присваивается целое число, чтобы показать, что мы присваиваем число с плавающей точкой, применяется точка:
1 2 |
double num1{ 1 }; // 1 — целочисленный литерал double num2{ 1. }; //1. — литерал числа с плавающей точкой |
Так, здесь число 1. представляет литерал числа с плавающей точкой, и в принципе аналогичен 1.0.
По умолчанию все такие числа с точкой расцениваются как числа типа double. Чтобы показать, что число представляет другой тип, для float применяется суффикс f/F, а для long double — l/L:
1 2 |
float num1{ 10.56f }; // float long double num2{ 10.56l }; // long double |
В качестве альтернативы также можно применять экспоненциальную запись:
1 2 |
double num1{ 5E3 }; // 5E3 = 5000.0 double num2{ 2.5e-3 }; // 2.5e-3 = 0.0025 |
Размеры типов данных
При перечислении типов данных указывался размер, который он занимает в памяти. Но стандарт языка устанавливает лишь минимальные значения, которые должны быть. Например, для типов int и short минимальное значение — 16 бит, для типа long — 32 бита, для типа long double — 64 разряда. При этом размер типа long должен быть не меньше размера типа int, а размер типа int — не меньше размера типа short, а размер типа long double должен быть не меньше double. А разработчики компиляторов могут выбирать предельные размеры для типов самостоятельно, исходя из аппаратных возможностей компьютера.
К примеру, компилятор g++ Windows для long double использует 16 байт. А компилятор в Visual Studio, который также работает под Windows, и clang++ под Windows для long double используют 8 байт. То есть даже в рамках одной платформы разные компиляторы могут по разному подходить к размерам некоторых типов данных. Но в целом используются те размеры, которые указаны выше при описании типов данных.
Однако бывают ситуации, когда необходимо точно знать размер определенного типа. И для этого в С++ есть оператор sizeof(), который возвращает размер памяти в байтах, которую занимает переменная:
1 2 3 4 5 6 7 |
#include <iostream>
int main() |
Консольный вывод при компиляции в g++:
sizeof(number) = 16 |
Символьные типы
В C++ есть следующие символьные типы данных:
- char: представляет один символ в кодировке ASCII. Занимает в памяти 1 байт (8 бит). Может хранить любое значение из диапазона от -128 до 127, либо от 0 до 255
- wchar_t: представляет расширенный символ. На Windows занимает в памяти 2 байта (16 бит), на Linux — 4 байта (32 бита). Может хранить любое значение из диапазона от 0 до 65 535 (при 2 байтах), либо от 0 до 4 294 967 295 (для 4 байт)
- char8_t: представляет один символ в кодировке Unicode. Занимает в памяти 1 байт. Может хранить любое значение из диапазона от 0 до 256
- char16_t: представляет один символ в кодировке Unicode. Занимает в памяти 2 байта (16 бит). Может хранить любое значение из диапазона от 0 до 65 535
- char32_t: представляет один символ в кодировке Unicode. Занимает в памяти 4 байта (32 бита). Может хранить любое значение из диапазона от 0 до 4 294 967 295
char
Переменная типа char хранит числовой код одного символа и занимает один байт. Стандарт языка С++ не определяет кодировку символов, которая будет использоваться для символов char, поэтому производители компиляторов могут выбирать любую кодировку, но обычно это ASCII.
В качестве значения переменная типа char может принимать один символ в одинарных кавычках, либо числовой код символа:
1 2 3 4 5 6 7 8 9 |
#include <iostream>
int main() |
В данном случае переменные a1 и a2 будут иметь одно и то же значение, так как 65 — это числовой код символа «A» в таблице ASCII. При выводе на консоль с помощью cout по умолчанию отображается символ.
Кроме того, в C++ можно использовать специальные управляющие последовательности, которые предваряются слешем и которые интерпретируются особым образом. Например, «\n» представляет перевод строки, а «\t» — табуляцию.
Однако ASCII обычно подходит для наборов символов языков, которые используют латиницу. Но если необходимо работать с символами для нескольких языков одновременно или с символами языков, отличных от английского, 256-символьных кодов может быть недостаточно. И в этом случае применяется Unicode.
Unicode (Юникод) — это стандарт, который определяет набор символов и их кодовых точек, а также несколько различных кодировок для этих кодовых точек. Наиболее часто используемые кодировки: UTF-8, UTF-16 и UTF-32. Разница между ними заключается в том, как представлена кодовая точка символа; числовое же значение кода для любого символа остается одним и тем же в любой из кодировок. Основные отличия:
- UTF-8 представляет символ как последовательность переменной длины от одного до четырех байт. Набор символов ASCII появляется в UTF-8 как однобайтовые коды, которые имеют те же значения кодов, что и в ASCII. UTF-8 на сегодняшний день является самой популярной кодировкой Unicode.
- UTF-16 представляет символы как одно или два 16-битных значения.
- UTF-32 представляет все символы как 32-битные значения
В C++ есть четыре типа для хранения символов Unicode: wchar_t, char8_t, char16_t и char32_t (char16_t и char32_t были добавлены в C++11, а char8_t — в C++20).
wchar_t
Тип wchar_t — это основной тип, предназначенный для наборов символов, размер которых выходит за пределы одного байта. Собственно отсюда и его название: wchar_t — wide (широкий) char. происходит от широкого символа, потому что этот символ «шире», чем обычный однобайтовый символ. Значения wchar_t определяются также как и символы char за тем исключением, что они предваряются символом «L»:
1 | wchar_t a1 {L’A’}; |
Также можно передать код символа
1 | wchar_t a1 {L’A’}; |
Значение, заключенное в одинарные кавычки, представляет собой шестнадцатеричный код символа. Обратная косая черта указывает на начало управляющей последовательности, а x после обратной косой черты означает, что код шестнадцатеричный.
Стоит учитывать, что для вывода на консоль символов wchar_t следует использовать не std::cout, а поток std::wcout:
1 2 3 4 5 6 7 8 |
#include <iostream>
int main() |
При этом поток std::wcout может работать как с char, так и с wchar_t. А поток std::cout для переменной wchar_t вместо символа будет выводить его числовой код.
Проблема с типом wchar_t заключается в том, что его размер сильно зависит от реализации и применяемой кодировки. Кодировка обычно соответствует предпочтительной кодировке целевой платформы. Так, для Windows wchar_t обычно имеет ширину 16 бит и кодируется с помощью UTF-16. Большинство других платформ устанавливают размер в 32 бита, а в качестве кодировки применяют UTF-32. С одной стороны, это позволяет больше соответствовать конкретной платформе. Но с другой стороны, затрудняет написание кода, переносимого на разные платформы. Поэтому в общем случае часто рекомендуется использовать типы char8_t, char16_t и char32_t. Значения этих типов предназначены для хранения символов в кодировке UTF-8, UTF-16 или UTF-32 соответственно, а их размеры одинаковы на всех распространенных платформах.
Для определения символов типов char8_t, char16_t и char32_t применяются соответственно префиксы u8, u и U:
1 2 3 |
char8_t c{ u8’l’ }; char16_t d{ u’l’ }; char32_t e{ U’o’ }; |
Стоит отметить, что для вывода на консоль значений char8_t/char16_t/char32_t пока нет встроенных инструментов типа std:cout/std:wcout.
Спецификатор auto
Иногда бывает трудно определить тип выражения. В этом случае можно предоставить компилятору самому выводить тип объекта. И для этого применяется спецификатор auto. При этом если мы определяем переменную со спецификатором auto, эта переменная должна быть обязательно инициализирована каким-либо значением:
1 2 3 |
auto number = 5; // number имеет тип int auto sum {1234.56}; // sum имеет тип double auto distance {267UL}; // distance имеет тип unsigned long |
На основании присвоенного значения компилятор выведет тип переменной. Неинициализированные переменные со спецификатором auto не допускаются:
1 | auto number; |
***
*https://codernet.ru/books/c_plus/samouchitel_c_s_primerami_i_zadachami_aleksandr_vasilev/
*https://metanit.com/cpp/tutorial/2.3.php
*https://intuit.ru/studies/courses/648/504/lecture/11421