Перейти к содержимому

Long long c что это

  • автор:

Числовые типы данных

До сих пор рассматривались только один тип целочисленных переменных — int . На самом деле существует несколько основных целочисленных типов, тип int — лишь один (но наиболее часто используемый) из них.

Таблица основных целочисленных типов.

Название Размер Знаковый Синонимы
short 2 байта Знаковый short int , signed short , signed short int
unsigned short 2 байта Беззнаковый unsigned short int
int 4 байта Знаковый signed int
unsigned 4 байта Беззнаковый unsigned int
long 4 байта Знаковый long int , signed long , signed long int
unsigned long 4 байта Беззнаковый unsigned long int
long long 8 байт Знаковый long long int , signed long long , signed long long int
unsigned long long 8 байт Беззнаковый unsigned long long int

То есть типы бывают “короткими” ( short ), обычными, длинными ( long ) и очень длинными ( long long ). Последний тип является расширением компилятора GNU C++ и не является стандартным типом для языка C++, поэтому он может отсутствовать в других реализациях языка или называться по-другому (например, в компиляторе Microsoft Visual C++ аналогичный тип называется int64 ). Чем “длиннее” тип, тем большее число различных значений он может принимать, тем больше памяти он занимает. Также типы бывают знаковыми ( signed ), которые могут принимать как положительные, так и отрицательные значения и беззнаковые ( unsigned ), которые принимают только неотрицательные значения.

Таблица значений, которые могут принимать различные типы:

Название Размер Минимальное значение Максимальное значение
short 16 бит -2 15 =- 32768 2 15 -1 = 32767
unsigned short 16 бит 0 2 16 -1 = 65535
int , long 32 бита -2 31 = -2147483648 2 31 -1 = 2147483647
unsigned , unsigned long 32 бита 0 2 32 -1 = 4294967295
long long 64 бита -2 63 = -9223372036854775808 2 63 -1 = 9223372036854775807
unsigned long long 64 бита 0 2 64 -1 = 18446744073709551615

На самом деле в стандарте языка C++ не оговорены конкретные значения размеров типов. Оговорено только то, что одинаковые знаковые и беззнаковые типы имеют одинаковые размеры, и размер меньшего типа всегда не превосходит размера большего типа. Вот какие размеры могут быть у этих типов в зависимости от разрядности процессора компьютера:

Тип 16-битный процессор 32-битный процессор 64-битный процессор
short 2 байта 2 байта 2 байта
int 2 байта 4 байта 4 байта
long 4 байта 4 байта 8 байт
long long 8 байт 8 байт

Действительные типы

Действительные (вещественные) числа представляются в виде чисел с десятичной точкой (а не запятой, как принято при записи десятичный дробей в русский текстах). Для записи очень больших или очень маленьких по модулю чисел используется так называемая запись “с плавающей точкой” (также называемая “научная” запись). В этом случае число представляется в виде некоторой десятичной дроби, называемой мантиссой, умноженной на целочисленную степень десяти (порядок). Например, расстояние от Земли до Солнца равно 1.496·10 11 , а масса молекулы воды 2.99·10 -23 .

Числа с плавающей точкой в программах на языке C++, а также при вводы и выводе записавыются в виде мантиссы, затем пишется буква e , затем пишется порядок. Пробелы внутри этой записи не ставятся. Например, указанные выше константы можно записать в виде 1.496e11 и 2.99e-23 . Перед самим числом также может стоять знак минус.

Для представления в памяти ЭВМ действительных чисел существует три типа:

Тип Точность Размер Количество знаков мантиссы Минимальное положительное значение Максимальное значение
float Одинарная 4 байта 7 1.4e-45 3.4e38
double Двойная 8 байт 15 5.0e-324 1.7e308
long double Расширенная 10 байт 19 1.9e-4951 1.1e4932

Операция деления

Для действительных чисел определены операции сложения, вычитания, умножения и деления.

При этом операция деления выполняется по-разному для переменных и констант целочисленного типа и для переменных и констант действительных типов. В первом случае деление производится нацело с отбрасыванием дробной части, во втором случае — деление производится точно и результатом является действительное число. Более точно, если делимое и делитель одновременно являются целочисленными константами или переменными целочисленных типов, то деление будет целочисленным, а если хотя бы одно из них действительное, то деление будет действительным. Например:

cout 

выведет 3 в первой строке и 3.33333 в остальных строках.

Результат выполнения деления не зависит от того, какой переменной будет присвоен результат. Если написать double a = 10 / 3; , то переменная a будет равна 3, так как деление 10/3 будет целочисленным, независимо от того, чему будет присвоен результат.

Приведение типов

Иногда возникает необходимость привести выражение одного типа к такому же выражению другого типа. Например, если есть две переменные a и b типа int и требуется вычислить их частное (не целочисленное) и записать в переменную d типа double . Следующий код:

double d; d = a / b

будет неверным, т.к. деление a / b будет целочисленным. Правильный код такой:

double d; d = (double)a / b;

В этом примере используется операция приведения типа: (double)a . Эта операция возвращает значение типа double , но равное значению переменной a . В результате деление будет выполняться, как деление вещественных чисел, поскольку будет выполняться деление действительного значения на целочисленное.

Операция приведения типа не меняет тип самой переменной и ее значение, а только возвращает значение другого типа.

Упражнение

Определите, чему будут равны следующие переменные

int a = 13 / 5; int b = 13 % 5; int c = 13.0 / 5; double d = 13 / 5; double e = 13 % 5; double f = 13.0 / 5; double g = 13 / 5 + 2 / 5; double h = 13.0 / 5 + 2.0 / 5; int i = 13.0 / 5 + 2.0 / 5;

Список функций

В стандартную математическую библиотеку языка Си (а, значит, и C++) входит множество специальных математических функций, которые нужно знать и уметь использовать. Для того, чтобы использовать эти функции в своей программе, необходимо подключить заголовочный файл, содержащий описания этих функций, что делается строчкой в начале программы:

#include

Функция от одного аргумента вызывается, например, так: sin(x) . Вместо числа x может быть любое число, переменная или выражение. Функция возращает значение, которое можно вывести на экран, присвоить другой переменной или использовать в выражении:

y = sin(x); cout 

Все перечисленные в таблице функции работают с типом double , этот тип считается основным типом для работы с действительными числами.

A: Целая часть

Дано положительное действительное число X. Выведите его целую часть.

B: Дробная часть

Дано положительное действительное число X. Выведите его дробную часть.

C: Первая цифра после точки

Дано положительное действительное число X. Выведите его первую цифру после десятичной точки. При решении этой задачи нельзя пользоваться условной инструкцией и циклом.

D: Гипотенуза

Даны длины катетов прямоугольного треугольника. Выведите длину его гипотенузы.

E: Площадь треугольника

Даны длины сторон треугольника. Вычислите площадь треугольника.

3 4 5
1 1 1
0.433013

F: Часы - 1

С начала суток прошло \(H\) часов, \(M\) минут, \(S\) секунд (\(0\le H 2 /6

По данному числу n вычислите сумму \(1+\frac+\frac+. +\frac\).

Решая эту задачу не забывайте, что 1/4==0 , 1/9==0 и т.д.

3 
1.36111

Знаете ли вы, что этот ряд сходится к \(\pi^2/6\)?

O: ln 2

По данному числу n вычислите сумму \(1-\frac12+\frac13-\frac14++. +\frac>\).

Операцией возведения в степень пользоваться нельзя. Алгоритм должен иметь сложность O(n). Попробуйте также обойтись без использования инструкции if.

3 
0.833333

Этот ряд сходится к значению ln 2.

P: Геометрическая прогрессия

Забудьте формулу суммы геометрической прогрессии и вычислите сумму \(1+x+x^2+. +x^n\).

Программа получает на вход целое число n и действительное число x. Операцией возведения в степень пользоваться нельзя. Алгоритм должен иметь сложность O(n).

4 0.1 
1.1111

Q: Просто π

По данному числу n вычислите сумму \( 4\left(1-\frac13+\frac15-\frac17+. +\frac\right)\)

Операцией возведения в степень пользоваться нельзя. Алгоритм должен иметь сложность O(n).

2 
3.46667

Этот ряд сходится к числу \(\pi\).

R: Экспонента

По данному целому числу n и действительному числу x вычислите сумму \( 1+\frac+\frac+\frac+. +\frac\)

Операцией возведения в степень пользоваться нельзя. Алгоритм должен иметь сложность O(n).

2 0.1 
1.105
10 0 
1
100 1 
2.718282

Этот ряд сходится к \(e^x\) при росте \(n\).

S: Косинус

По данному целому числу n и действительному числу x вычислите сумму \( 1-\frac+\frac-\frac+. +(-1)^n\frac>\)

Операцией возведения в степень пользоваться нельзя. Алгоритм должен иметь сложность O(n).

2 0.1 
0.995004
10 0 
1
50 3.14159 
-1

Этот ряд сходится к \(\cos x\) при росте \(n\) (углы измеряются в радианах).

T: Сумма с корнями

По данным натуральным числам n и a вычислите сумму \[ \sqrt> > > \]

3 2 
2.13063

U: Схема Горнера

Дан многочлен \(P(x)=a_nx^n+a_x^+. +a_1x+a_0\) и число \(x\). Вычислите значение этого многочлена, воспользовавшись схемой Горнера: \[ P(x)= \left( . \left( \left( \left( a_n x + a_ \right) x + a_ \right) x + a_ \right) . \right) x + a_ \]

Сначала программе подается на вход целое неотрицательное число \(n\le20\), затем действительное число \(x\), затем следует \(n+1\) вещественное число — коэффициенты многочлена от старшего к младшему. Программа должна вывести значение многочлена.

При решении этой задачи нелья использовать массивы и операцию возведения в степень. Программа должна иметь сложность O(n).

1 0
1 1
1
2 0.5
1 1 1
1.75

V*: Система линейных уравнений - 1

Даны числа \(a\), \(b\), \(c\), \(d\), \(e\), \(f\). Известно, что система линейных уравнений \[ \cases \]

имеет ровно одно решение. Выведите два числа \(x\) и \(y\), являющиеся решением этой системы.

1 0 0 1 3 3 
3 3

W*: Первая цифра после точки

Попробуйте еще раз решить следующую задачу.

Дано положительное действительное число X. Выведите его первую цифру после десятичной точки. При решении этой задачи нельзя пользоваться условной инструкцией и циклом.

Если у вас не получается решить эту задачу, прочите примечание в конце листка.

X*: Часы - 3

Вспомните задачу K и попробуйте еще раз решить следующую задачу.

С начала суток часовая стрелка повернулась на угол в \(\alpha\) градусов. Определите сколько полных часов, минут и секунд прошло с начала суток, то есть решите задачу, обратную задаче F. Запишите ответ в три целочисленные переменные и выведите их на экран.

При решении этой задачи нельзя пользоваться условными инструкциями и циклами.

Если у вас не получается решить эту задачу, прочите примечание в конце листка.

31.025
1 2 3

Y**: Система линейных уравнений - 2

Даны числа \(a\), \(b\), \(c\), \(d\), \(e\), \(f\). Решите систему линейных уравнений \[ \cases \]

Вывод программы зависит от вида решения этой системы.

Если система не имеет решений, то программа должна вывести единственное число 0 .

Если система имеет бесконечно много решений, каждое из которых имеет вид \(y=kx+b\), то программа должна вывести число 1 , а затем значения \(k\) и \(b\).

Если система имеет единственное решение \((x_0,y_0)\), то программа должна вывести число 2 , а затем значения \(x_0\) и \(y_0\).

Если система имеет бесконечно много решений вида \(x=x_0\), \(y\) — любое, то программа должна вывести число 3 , а затем значение \(x_0\).

Если система имеет бесконечно много решений вида \(y=y_0\), \(x\) — любое, то программа должна вывести число 4 , а затем значение \(y_0\).

Если любая пара чисел \((x,y)\) является решением, то программа должна вывести число 5 .

Тесты к этой задаче закрытые.

1 0 0 1 3 3 
2 3 3
1 1 2 2 1 2 
1 -1 1
0 2 0 4 1 2 
4 0.5

Z**: Кубическое уравнение

Даны четыре действительных числа: \(A\), \(B\), \(C\), \(D\). Найдите все корни уравнения \(Ax^3+Bx^2+Cx+D=0\).

Известно, что все корни этого уравнения не превосходят по абсолютной величине 1000. Известно, что любые два корня этого уравнения различаются не менее, чем на 10 -6 .

Программа получает на вход четыре действительных числа: \(A\), \(B\), \(C\), \(D\). Любые из этих четырех чисел, но не все одновременно, могут быть равны 0.

Программа должна вывести от 0 до 3 действительных чисел: корни данного уравнения в порядке возрастания. Кратные корни должны быть выведены только один раз. Значения корней необходимо выводить с точностью до 6 знаков после точки.

1 -2 1 0

Тесты к этой задаче закрытые.

Если у вас не получаются задачи.

Если вам не удается решить задачи K, W, X, то посмотрите на следующую программу:

#include using namespace std; int main()

Инструкция cout.precision(20) устанавливает количество значащих цифр при выводе действительных чисел. Откомпилируйте и запустите эту программу, подумайте над результатом ее работы. Подумайте, как это может сказаться на решении заданий K, W, X.

Типы данных в языке 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++ можно также почитать здесь.

Спасибо за внимание, удачи! Буду благодарен за отзывы в комментариях к этой статье.

Типы данных в языке 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

C2017/Типы данных в C

Целочисленные типы данных используются для хранения целых чисел (тип char также используется для хранения ASCII-символов). Все размеры диапазонов представленных ниже типов данных минимальны и на отдельно взятой платформе могут быть больше.

Минимальный диапазон значений целых типов по стандарту определяется с [math]-(2^N-1)[/math] по [math]2^N-1[/math] , где [math]N[/math] — разрядность типа. Реализация компиляторов может расширять этот диапазон по своему усмотрению. Отметим, что такие ограничения позволяют использовать то или иное представление знаковых чисел:

  • дополнительный код (two’s complement) — для отрицательного числа код можно получить инвертированием его двоичного модуля и прибавлением к инверсии единицы
  • обратный код (ones’ complement) — двоичный код отрицательного целого числа состоит из одноразрядного кода знака (двоичной цифры 1), за которым следует ( [math]n−1[/math] )-разрядное двоичное число, представляющее собой инвертированное ( [math]n−1[/math] )-разрядное представление модуля числа
  • прямой код (sign and magnitude method) — знаковый бит и затем модуль числа без инвертирования.

Отдельное внимание стоит уделить типу char . Формально это отдельный тип, но фактически char эквивалентен либо signed char , либо unsigned char , в зависимости от компилятора.

Для того, чтобы избежать путаницы между размерами типов стандарт C99 ввел новые типы данных, описанные в файле stdint.h. Среди них такие типы как: intN_t , int_leastN_t , int_fastN_t , где N = 8, 16, 32 или 64. Приставка least- обозначает минимальный тип, способный вместить N бит, приставка fast- обозначает тип размером не менее 16 бит, работа с которым наиболее быстрая на данной платформе. Типы без приставок обозначают типы с фиксированном размером, равным N бит.

Типы с приставками least- и fast- можно считать заменой типам int , short , long , с той лишь разницей, что первые дают программисту выбрать между скоростью и размером.

Тип данных Размер Минимальный диапазон значений Первое появление
signed char минимум 8 бит от -127 (= -(2 8 -1)) до 127 K&R C
unsigned char минимум 8 бит от 0 до 255 (=2 8 -1) K&R C
char минимум 8 бит от -127 до 127 или от 0 до 255 в зависимости от компилятора K&R C
short int минимум 16 бит от -32,767 (= -(2 15 -1)) до 32,767 K&R C
unsigned short int минимум 16 бит от 0 до 65,535 (= 2 16 -1) K&R C
int минимум 16 бит от -32,767 до 32,767 K&R C
unsigned int минимум 16 бит от 0 до 65,535 (= 2 16 -1) K&R C
long int минимум 32 бита от -2,147,483,647 до 2,147,483,647 K&R C
unsigned long int минимум 32 бита от 0 до 4,294,967,295 (= 2 32 -1) K&R C
long long int минимум 64 бита от -9,223,372,036,854,775,807 до 9,223,372,036,854,775,807 C99
unsigned long long int минимум 64 бита от 0 до 18,446,744,073,709,551,615 (= 2 64 -1) C99
int8_t 8 бит от -127 до 127 C99
uint8_t 8 бит от 0 до 255 (=2 8 -1) C99
int16_t 16 бит от -32,767 до 32,767 C99
uint16_t 16 бит от 0 до 65,535 (= 2 16 -1) C99
int32_t 32 бита от -2,147,483,647 до 2,147,483,647 C99
uin32_t 32 бита от 0 до 4,294,967,295 (= 2 32 -1) C99
int64_t 64 бита от -9,223,372,036,854,775,807 до 9,223,372,036,854,775,807 C99
uint64_t 64 бита от 0 до 18,446,744,073,709,551,615 (= 2 64 -1) C99
Ошибки
#include int main() { unsigned int a = 1000; int b = -1; printf("%s is bigger\n", (a > b ? "a" : "b")); return 0; }

Memsize-типы

Для представления величин, связанных с памятью, в C есть typedef'ы size_t и ptrdiff_t. Размер определяется архитектурой процессора.

#include

size_t — беззнаковый тип для представления размера любого объекта (включая массив). Он способен хранить в себе размер максимального массива, который может быть теоретически выделен в рамках данной архитектуры.

Оператор sizeof возвращает результат типа size_t. Для size_t гарантируется, что он как минимум 16-битный.

Тип ptrdiff_t знаковый, используется для хранения разности указателей. Стандартом допускается вычисление разности только для указателей одного типа, для разных типов — зависит от реализации.

Модели данных

Под моделью данных следует понимать соотношения размерностей типов, принятых в рамках среды разработки на С.

Примеры приведены в таблице.

Data-model.png

Вещественные типы

Стандартом гарантируется только, что sizeof(float)

На большинстве платформ применяются типы с плавающей точкой в соответствии со стандартом IEEE 754.

Логический тип

В языке C в стандарте C99 введён тип _Bool.

#include #include int main() { bool b = 123456; printf("%s\n", (b == 1) ? "true" : "false"); return 0; }

Массивы

Для каждого типа T, кроме void и типов функций, существует тип «массив из элементов N типа T». Массив - это коллекция значений одного типа, хранящихся последовательно в памяти. Массив размера N индексируется целым числом от 0 до N-1.

int cat[10] = {5,7,2}; // массив из 10 элементов, каждый типа int

Массивы могут быть инициализированы с помощью списка инициализации, но не могут быть присвоены друг к другу.

int a[10][8]; // массив из 10 элементов, каждый типа 'массив из 8 int элементов' float f[][32] = {{0},{4,5,6}};

В C99 добавлены динамические многомерные массивы:

double (*A)[n] = malloc(sizeof(double[n][n]));
#include #include #include void Fill(int n, int m, int a[][m]) { int x = 0; for (int i = 0; i  n; ++i) { for (int j = 0; j  m; ++j) { a[i][j] = ++x; } } } void Print(int n, int m, int a[][m]) { for (int i = 0; i  n; ++i) { for (int j = 0; j  m; ++j) { printf("%3d", a[i][j]); } putchar('\n'); } } int main() { int n, m; scanf("%d %d", &n, &m); int (*a)[m] = malloc(sizeof(int[n][m])); Fill(n, m, a); Print(n, m, a); free(a); int b[n][m]; Fill(n, m, b); Print(n, m, b); return 0; }

Указатели

Для любого типа T существует тип «указатель на T».

Переменные могут быть объявлены как указатели на значения различных типов с помощью символа * . Для того чтобы определить тип переменной как указатель, нужно предварить её имя звёздочкой.

char letterC = 'C'; char *letter = &letterC; //взятие адреса переменной letterC и присваивание в переменную letter printf("This code is written in %c.", *letter); //"This code is written in C."

Поскольку указатель — тоже тип переменной, правило «для любого типа T» выполняется и для них: можно объявлять указатели на указатели. К примеру, можно пользоваться int*** :

int w = 100; int *x = &w; int **y = &x; int ***z = &y; printf("w contains %d.", ***z); //"w contains 100."

Существуют также указатели на массивы и на функции. Указатели на массивы имеют следующий синтаксис:

char *pc[10]; // массив из 10 указателей на char char (*pa)[10]; // указатель на массив из 10 переменных типа char

pc — массив указателей, занимающий 10 * sizeof(char*) байт (на распространённых платформах — обычно 40 или 80 байт), а pa — это один указатель; занимает он обычно 4 или 8 байт, однако позволяет обращаться к массиву, занимающему 10 байт: sizeof(pa) == sizeof(int*) , но sizeof(*pa) == 10 * sizeof(char) . Указатели на массивы отличаются от указателей на первый элемент арифметикой. Например, если указатели pa указывает на адрес 2000, то указатель pa+1 будет указывать на адрес 2010.

char (*pa)[10]; char array[10] = "Wikipedia"; pa = &array; printf("An example for %s.\n", *pa); //"An example for Wikipedia." printf("%c %c %c", (*pa)[1], (*pa)[3], (*pa)[7]); //"i i i"

Структуры

Композитный тип данных, инкапсулирующий без сокрытия набор значений различных типов.

При инициализации элементов структуры надо иметь в виду следующий момент:

If there are fewer initializers in a brace-enclosed list than there are elements or members of an aggregate, or fewer characters in a string literal used to initialize an array of known size than there are elements in the array, the remainder of the aggregate shall be initialized implicitly the same as objects that have static storage duration.

Важное отличие между С и C++.

struct Point { int x; int y; };

В C++ можно писать

Point pt;

В C нужно писать

struct Point pt;

Так сделано специально. Названия структур находятся в отдельном пространстве имён. Их всего четыре вида:

  • названия меток (для goto);
  • теги (названия структур, объединений, перечислений);
  • члены структур и объединений;
  • все прочие идентификаторы.

Это позволяет, например, назвать переменную так же, как называется структура.

Или же можно сделать typedef:

typedef struct { int x; int y; } Point;

Доступ к элементам структуры осуществляется с помощью оператора .. Для обращения к полям структуры по указателю существует оператор «стрелочка» ->, синонимичный предыдущей записи: (*p).x — то же самое, что и p->x.

Структуры могут содержать указатели на самих себя, что позволяет реализовывать многие структуры данных, основанных на связных списках. Такая возможность может показаться противоречивой, однако все указатели занимают одинаковое число байт, поэтому размер этого поля не изменится от числа полей структуры.

Структуры не всегда занимают число байт, равное сумме байт их элементов. Компилятор обычно выравнивает элементы. The Lost Art of C Structure Packing

Адрес памяти первого поля структуры равен адресу самой структуры.

Объединения

Доступ к одним и тем же данным разными способами:

#include struct Point { union { struct XYZ { float x; float y; float z; }; float coords[3]; }; }; int main() { struct Point pt = {.x = 1.f, .y = 2.f, .z = 3.f}; printf("%zu\n", sizeof(pt)); for (int i = 0; i  3; ++i) { printf("%f\n", pt.coords[i]); } return 0; }

Т. н. type punning (каламбур типизации)

#include #include float ChangeSign(float x) { union { float floatValue; uint32_t intValue; } u; u.floatValue = x; u.intValue ^= (1U  31); return u.floatValue; } int main() { printf("%f\n", ChangeSign(1.25f)); return 0; }

Стандартом C99 это разрешается (в отличие от приведения указателей разных типов).

If the member used to read the contents of a union object is not the same as the member last used to store a value in the object, the appropriate part of the object representation of the value is reinterpreted as an object representation in the new type as described in 6.2.6 (a process sometimes called ‘‘type punning’’). This might be a trap representation.

Битовые поля

Перечисления

enum Color { RED, GREEN, BLUE, }; enum Shape { SQUARE, TRIANGLE, }; int main() { enum Color color = SQUARE; // even no warning }

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *