C ++

Стандартные преобразования C ++

Стандартные преобразования C ++
В C ++ есть два типа сущностей: базовые типы и составные типы. Основные типы - это скалярные типы. Составные типы - это остальные типы сущностей. Преобразование может происходить из одного типа объекта в другой соответствующий тип. Рассмотрим следующую программу:

#включать
#включать
используя пространство имен std;
int main ()

int rt1 = sqrt (5);
int rt2 = sqrt (8);
cout<возврат 0;

На выходе 2, 2, Это означает, что программа вернула квадратный корень из 5 как 2 и квадратный корень из 8 также как 2. Итак, первые два утверждения в основной() Функция вычислила ответы квадратного корня из 5 и квадратного корня из 8. В этой статье не обсуждаются пол или потолок на языке C++. Скорее, в этой статье обсуждается преобразование одного типа C ++ в другой подходящий тип C ++; с указанием любого приближения в сделанном значении, потери точности или ограничения, добавленного или удаленного. Базовые знания C ++ - необходимое условие для понимания этой статьи.

Содержание статьи

  • Интегральные преобразования
  • Преобразования с плавающей запятой
  • Преобразования с плавающей запятой в целые числа
  • Рейтинг целочисленных преобразований
  • Интегральные акции
  • Обычные арифметические преобразования
  • Продвижение с плавающей точкой
  • Указатель преобразования
  • Преобразование функции в указатель
  • Логические преобразования
  • Lvalue, prvalue и xvalue
  • Xvalue
  • Преобразования Lvalue-to-Rvalue
  • Преобразования массива в указатель
  • Преобразование функции в указатель
  • Временные преобразования материализации
  • Квалификационные преобразования
  • Заключение

Интегральные преобразования

Целочисленные преобразования - это целочисленные преобразования. Беззнаковые целые числа включают «unsigned char», «unsigned short int», «unsigned int», «unsigned long int» и «unsigned long long int».Соответствующие целые числа со знаком включают «signed char», «short int», «int», «long int» и «long long int».”Каждый тип int должен занимать столько же байтов, сколько и его предшественник. Для большинства систем один тип объекта может быть преобразован в соответствующий тип без каких-либо проблем. Проблема возникает при преобразовании типа большего диапазона в тип меньшего диапазона или при преобразовании числа со знаком в соответствующее число без знака.

У каждого компилятора есть максимальное значение, которое он может принять для короткого int. Если короткому int присваивается число выше этого максимума, предназначенное для int, компилятор будет следовать некоторому алгоритму и вернет число в диапазоне короткого int. Если программисту повезет, компилятор предупредит о проблемах с использованием несоответствующего преобразования. То же объяснение применимо к преобразованиям других типов int.

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

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

Любое целое число, кроме 0, может быть преобразовано в логическое значение true. 0 преобразуется в логическое значение false. Следующий код иллюстрирует это:

int a = -27647;
поплавок b = 2.5;
int c = 0;
bool a1 = a;
bool b1 = b;
bool c1 = c;
cout<cout<cout<Результат:

1 для истины
1 для истины
0 для ложного

Преобразования с плавающей запятой

Типы с плавающей запятой включают «float», «double» и «long double».»Типы с плавающей запятой не группируются на знаковые и беззнаковые, как целые числа. Каждый тип может иметь знаковое или беззнаковое число. Тип с плавающей запятой должен иметь как минимум такую ​​же точность, что и его предшественник. То есть, «long double» должен иметь точность, равную или большую, чем «double», а «double» должен иметь такую ​​же или большую точность, как «float».”

Помните, что диапазон типа с плавающей запятой не является непрерывным; скорее, это маленькими шагами. Чем выше точность типа, тем меньше шаги и больше количество байтов для хранения числа. Таким образом, когда число с плавающей запятой преобразуется из типа с более низкой точностью в тип с более высокой точностью, программист должен принять ложное увеличение точности и возможное увеличение количества байтов для хранения чисел. Когда число с плавающей запятой преобразуется из типа с более высокой точностью в тип с более низкой точностью, программист должен принять потерю точности. Если количество байтов для хранения чисел должно быть уменьшено, тогда компилятор будет следовать некоторому алгоритму и вернет число в качестве замены (что, вероятно, не то, что хочет программист). Также помните о проблемах, выходящих за пределы допустимого диапазона.

Преобразования с плавающей запятой в целые числа

Число с плавающей запятой преобразуется в целое путем отсечения дробной части. Следующий код иллюстрирует это:

поплавок f = 56.953;
int i = f;
cout<На выходе 56. Диапазоны для чисел с плавающей запятой и целых чисел должны быть совместимы.

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

Рейтинг целочисленных преобразований

Любой целочисленный тип имеет присвоенный ему ранг. Этот рейтинг способствует конверсии. Рейтинг относительный; ранги не на фиксированных уровнях. За исключением char и signed char, никакие два целых числа со знаком не имеют одинаковый ранг (при условии, что char подписан). Беззнаковые целочисленные типы имеют такой же рейтинг, как и их соответствующие целочисленные типы со знаком. Рейтинг выглядит следующим образом:

  • Предполагая, что char подписан, тогда char и signed char имеют одинаковый ранг.
  • Ранг целочисленного типа со знаком больше, чем ранг целочисленного типа со знаком меньшего количества байтов памяти. Таким образом, ранг long long int со знаком больше, чем ранг long int со знаком, который больше ранга signed int, который больше, чем ранг короткого int со знаком, который больше ранга signed char.
  • Ранг любого целочисленного типа без знака равен рангу соответствующего целочисленного типа со знаком.
  • Ранг unsigned char равен рангу подписанного char.
  • bool имеет наименьший ранг; его ранг меньше, чем у подписанного символа.
  • char16_t имеет тот же ранг, что и short int. char32_t имеет тот же ранг, что и int. Для компилятора g ++ wchar_t имеет тот же ранг, что и int.

Интегральные акции

Интегральные акции - это целочисленные акции. Нет причин, по которым целое число из меньшего количества байтов не может быть представлено целым числом из большего количества байтов. Integer Promotions занимается всем, что следует ниже:

  • Подписанный короткий int (два байта) может быть преобразован в подписанный int (четыре байта). Беззнаковое короткое int (два байта) может быть преобразовано в беззнаковое int (четыре байта). Примечание: преобразование короткого int в long int или long long int приводит к потере байтов хранилища (местоположения объекта) и потере памяти. Bool, char16_t, char32_t и wchar_t исключены из этой акции (с компилятором g ++ char32_t и wchar_t имеют одинаковое количество байтов).
  • С помощью компилятора g ++ тип char16_t можно преобразовать в тип int со знаком или тип int без знака; тип char32_t может быть преобразован в тип int со знаком или тип int без знака; и тип wchar_t может быть преобразован в тип int со знаком или без знака.
  • Тип bool можно преобразовать в тип int. В этом случае true становится 1 (четыре байта), а false становится 0 (четыре байта). Int может быть подписан или подписан.
  • Целочисленное продвижение также существует для типа перечисления с незаданной областью - см. Ниже.

Обычные арифметические преобразования

Рассмотрим следующий код:

поплавок f = 2.5;
int i = f;
cout<Код компилируется без указания каких-либо предупреждений или ошибок, давая вывод 2, что, вероятно, не то, что ожидалось. = является бинарным оператором, потому что он принимает левый и правый операнды. Рассмотрим следующий код:

int i1 = 7;
int i2 = 2;
float flt = i1 / i2;
cout<На выходе 3, но это неправильно; это должно было быть 3.5. Оператор деления / также является бинарным оператором.

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

  • Если один из операндов имеет тип long double, то другой будет преобразован в long double.
  • В противном случае, если один из операндов является двойным, другой будет преобразован в двойной.
  • В противном случае, если один из операндов является плавающим, другой будет преобразован в плавающий. В приведенном выше коде результат i1 / i2 официально равен 2; вот почему flt равно 2. Результат двоичного файла / применяется как правый операнд к двоичному оператору =. Итак, окончательное значение 2 - это число с плавающей запятой (не int).

Иначе, ЦЕЛОЕ ПРОДВИЖЕНИЕ БУДЕТ ПРОИЗОЙДЕНО СЛЕДУЮЩИМ:

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

Продвижение с плавающей точкой

Типы с плавающей запятой включают «float», «double» и «long double»."Тип с плавающей запятой должен иметь, по крайней мере, такую ​​же точность, как и его предшественник. Повышение с плавающей запятой позволяет преобразовать float в double или из double в long double.

Указатель преобразования

Указатель одного типа объекта не может быть назначен указателю другого типа объекта. Следующий код не компилируется:

int id = 6;
int * intPtr = &id;
float idf = 2.5;
поплавок * floatPtr = &idf;
intPtr = floatPtr; // ошибка здесь

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

int id = 6;
int * intPtr = &id;
intPtr = 0;
float idf = 2.5;
поплавок * floatPtr = &idf;
floatPtr = 0;
intPtr = floatPtr; // ошибка здесь

Константа нулевого указателя одного типа объекта не может быть назначена константе нулевого указателя другого типа объекта. Следующий код не компилируется:

int id = 6;
int * intPtr = &id;
int * const intPC = 0;
float idf = 2.5;
поплавок * floatPtr = &idf;
float * const floatPC = 0;
intPC = floatPC; // ошибка здесь

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

float idf = 2.5;
float * floatPtr = 0;
floatPtr = &idf;
cout<<*floatPtr<<'\n';

На выходе 2.5.

Как и ожидалось, константе нулевого указателя нельзя присвоить какое-либо значение адреса ее типа. Следующий код не будет компилироваться:

float idf = 2.5;
float * const floatPC = 0;
floatPC = &idf; // ошибка здесь

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

float idf = 2.5;
float * const floatPC = 0;
float * floatPter = &idf;
floatPter = floatPC; //ОК
cout << floatPter << '\n';

На выходе 0.

Два значения нулевого указателя одного типа сравниваются (==) равны.

Указатель на тип объекта может быть назначен указателю на void. Следующий код иллюстрирует это:

float idf = 2.5;
поплавок * floatPtr = &idf;
void * vd;
vd = floatPtr;

Код компилируется без предупреждения или сообщения об ошибке.

Преобразование функции в указатель

Указатель на функцию, которая не вызовет исключения, может быть назначен указателю на функцию. Следующий код иллюстрирует это:

#включать
используя пространство имен std;
void fn1 () noexcept

cout << "with noexcept" << '\n';

void fn2 ()

//заявления

void (* func1) () noexcept;
void (* func2) ();
int main ()

func1 = &fn1;
func2 = &fn2;
func2 = &fn1;
func2 ();
возврат 0;

На выходе без исключений.

Логические преобразования

В C ++ сущности, которые могут привести к ложному результату, включают «ноль», «нулевой указатель» и «нулевой указатель на член».”Все остальные сущности приводят к истинному. Следующий код иллюстрирует это:

bool a = 0.0; cout << a <<'\n';
float * floatPtr = 0;
bool b = floatPtr; cout << b <<'\n';
bool c = -2.5; cout << c <<'\n';
bool d = +2.5; cout << d <<'\n';

Результат:

0 // для false
0 // для false
1 // для истины
1 // для истины

Lvalue, prvalue и xvalue

Рассмотрим следующий код:

int id = 35;
int & id1 = id;
cout << id1 << '\n';

На выходе 35 год. В коде id и id1 являются l-значениями, потому что они идентифицируют местоположение (объект) в памяти. Выход 35 - это prvalue. Любой литерал, кроме строкового, является prvalue. Другие значения prvalue не так очевидны, как в следующих ниже примерах. Рассмотрим следующий код:

int id = 62;
int * ptr = &id;
int * pter;

Ptr - это lvalue, потому что он определяет местоположение (объект) в памяти. С другой стороны, pter не является lvalue. Pter - это указатель, но он не идентифицирует какое-либо место в памяти (он не указывает на какой-либо объект). Итак, pter - это prvalue.

Рассмотрим следующий код:

void fn ()

//заявления

void (* func) () = &fn;
float (* functn) ();

Fn () и (* func) () являются выражениями lvalue, потому что они идентифицируют сущность (функцию) в памяти. С другой стороны, (* functn) () не является выражением lvalue. (* functn) () - указатель на функцию, но он не идентифицирует какой-либо объект в памяти (он не указывает на какую-либо функцию в памяти). Итак, (* functn) () - это выражение prvalue.

Теперь рассмотрим следующий код:

структура S

int n;
;
S obj;

S - это класс, а obj - это объект, созданный из класса. Obj идентифицирует объект в памяти. Класс - это обобщенная единица. Итак, S на самом деле не идентифицирует какой-либо объект в памяти. S называется безымянным объектом. S также является выражением prvalue.

В этой статье основное внимание уделяется prvalues. Prvalue означает чистое rvalue.

Xvalue

Xvalue означает истекающую стоимость. Временные значения устаревают. Lvalue может стать xvalue. Prvalue также может стать xvalue. В этой статье основное внимание уделяется prvalues. Xvalue - это lvalue или безымянная ссылка rvalue, хранилище которой можно повторно использовать (обычно потому, что срок его существования приближается к концу). Рассмотрим следующий код, который работает:

структура S

int n;
;
int q = S ().n;

Выражение «int q = S ().n; » копирует любое значение n в q. S () - это просто средство; это не часто используемое выражение. S () - это prvalue, использование которого преобразовало его в xvalue.

Преобразования Lvalue-to-Rvalue

Рассмотрим следующее утверждение:

int ii = 70;

70 - это prvalue (rvalue), а ii - это lvalue. Теперь рассмотрим следующий код:

int ii = 70;
int tt = ii;

Во втором утверждении ii находится в ситуации prvalue, поэтому ii становится prvalue там. Другими словами, компилятор неявно преобразует ii в prvalue. То есть, когда lvalue используется в ситуации, когда реализация ожидает prvalue, реализация преобразует lvalue в prvalue.

Преобразования массива в указатель

Рассмотрим следующий код, который работает:

char * p;
char q [] = 'a', 'b', 'c';
р = & q [0];
++п;
cout<<*p<<'\n';

На выходе б. Первый оператор - это выражение и указатель на символ. Но на какой символ указывает утверждение? - Нет персонажа. Итак, это prvalue, а не lvalue. Второй оператор представляет собой массив, в котором q [] является выражением lvalue. Третья инструкция превращает prvalue, p, в выражение lvalue, которое указывает на первый элемент массива.

Преобразование функции в указатель

Рассмотрим следующую программу:

#включать
используя пространство имен std;
void (* func) ();
void fn ()

//заявления

int main ()

func = &fn;
возврат 0;

Выражение «void (* func) ();» указатель на функцию. Но на какую функцию указывает выражение? - Не работает. Итак, это prvalue, а не lvalue. Fn () - определение функции, где fn - выражение lvalue. В main () «func = &fn;»Превращает prvalue, func, в выражение lvalue, которое указывает на функцию, fn ().

Временные преобразования материализации

В C ++ prvalue можно преобразовать в xvalue того же типа. Следующий код иллюстрирует это:

структура S

int n;
;
int q = S ().n;

Здесь prvalue, S (), было преобразовано в xvalue. В качестве значения x это не продлится долго - см. Более подробное объяснение выше.

Квалификационные преобразования

Тип cv-qualified - это тип, определяемый зарезервированным словом «const» и / или зарезервированным словом «volatile.”

CV-квалификация также оценивается. Никакая cv-квалификация не меньше квалификации «const», которая меньше квалификации «const volatile». Никакая cv-квалификация не меньше квалификации "volatile", которая меньше квалификации "const volatile". Итак, существует два потока квалификационного ранжирования. Один тип может быть более квалифицированным, чем другой.

Тип с более низким значением prvalue cv может быть преобразован в тип prvalue с более низким значением cv. Оба типа должны быть указателем на cv.

Заключение

Сущности C ++ могут быть неявно или явно преобразованы из одного типа в связанный. Однако программист должен понимать, что можно преобразовать, а что нельзя, и в какую форму. Преобразование может происходить в следующих областях: интегральные преобразования, преобразования с плавающей запятой, преобразования с плавающей запятой в целые числа, обычные арифметические преобразования, преобразования указателя, преобразования функции в указатель, логические преобразования, преобразования L-значения в r-значение, преобразования массива в указатель. , Преобразования функции в указатель, преобразования временной материализации и преобразования квалификаций.

Игры HD Remastered для Linux, ранее не выпускавшиеся для Linux
Многие разработчики и издатели игр придумывают HD-ремастеры старых игр, чтобы продлить жизнь франшизы, порадовать фанатов, требующих совместимости с с...
Как использовать AutoKey для автоматизации игр под Linux
AutoKey - это утилита автоматизации рабочего стола для Linux и X11, запрограммированная на Python 3, GTK и Qt. Используя его функции сценариев и MACRO...
Как показать счетчик FPS в играх для Linux
Игры для Linux получили серьезный толчок, когда Valve объявила о поддержке Linux для клиента Steam и своих игр в 2012 году. С тех пор многие игры AAA ...