Reinterpret cast c что это
Перейти к содержимому

Reinterpret cast c что это

  • автор:

C++ Casts

In C++ programming, it is not recommended to use the C style casting because C style casting often has ambiguous meaning to the programmer. There are four common types of casting in C++, static_cast , const_cast , dynamic_cast , and reinterpret_cast .

In this blog post, I would like to discuss some of the basic usages of the C++ casts by giving some concrete examples.

Static Cast

static_cast is commonly used to cast between numerical types and covert void* to pointers pointing to a variable of a certain type. The user should pay attention to the range of the numerical type. For example, casting a valid large long typed variable to uint8_t typed variable would not give you the desired result since uint8_t could not hold large values.

In addition, static_cast checks type conversion compatibility at compile-time, but there is no runtime check. So it could be dangerous for some pointer type conversions.

To compile the program, please run the following command in the terminal.

The expected output of the program would be as follows.

Const Cast

const_cast is commonly used to cast away the const specifier for any const typed pointers. With the const specifier, the user is not allowed to modify the value of the variable which the pointer points to via dereferencing the pointer. Once the const specifier for the pointer is cast away by const_cast , we could modify the value of the variable via dereferencing the pointer, as long as the variable is modifiable.

To compile the program, please run the following command in the terminal.

The expected output of the program would be as follows.

Dynamic Cast

dynamic_cast is commonly used to cast between the pointers to the classes up, down, and sideways along the inheritance hierarchy. It does runtime check. If it turns out to be an invalid cast after runtime check, a nullptr would be returned.

To compile the program, please run the following command in the terminal.

The expected output of the program would be as follows.

Reinterpret Cast

reinterpret_cast is commonly used for pointer cast. It asks the compiler to treat the expression as if it has the new type. It would not check anything so it is the most dangerous cast.

To compile the program, please run the following command in the terminal.

The expected output of the program would be as follows.

The long type and long long type are the exact same 64-bit integer type on my computer.

reinterpret_cast can also be casted to reference and it is equivalent as casting to a pointer followed by dereference. Other than casting to pointer and reference, reinterpret_cast can only do a limited number of casts.

Conclusions

Understand exactly the cast you are trying to use before putting it into your code.

Еще раз про приведение типов в языке С++ или расстановка всех точек над cast


Этот пост попытка кратко оформить все, что я читал или слышал из разных источников про операторы приведения типов в языке C++. Информация ориентирована в основном на тех, кто изучает C++ относительно недолго и, как мне кажется, должна помочь понять cпецифику применения данных операторов. Старожилы и гуру С++ возможно помогут дополнить или скорректировать описанную мной картину. Всех интересующихся приглашаю под кат.

Приведение типов в стиле языка C (C-style cast)

Приведение типов в стиле языка C может привести выражение любого типа к любому другому типу данных (исключение это приведение пользовательских типов по значению, если не определены правила их приведения, а также приведение вещественного типа к указателю или наоборот). К примеру, unsigned int может быть преобразован к указателю на double. Данный метод приведения типов может быть использован в языке C++. Однако, метод приведения типов в стиле языка C не делает проверки типов на совместимость, как это могут сделать static_cast и dynamic_cast на этапе компиляции и на этапе выполнения соответственно. При этом все, что умеют const_cast и reinterpret_cast данный метод приведения типов делать может.

Общий вид приведения:

(new_type)exp

, где new_type – новый тип, к которому приводим, а exp – выражение, которое приводится к новому типу.

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

const_cast

Оператор приведения const_cast удаляет или добавляет квалификаторы const и volatile с исходного типа данных (простые типы, пользовательские типы, указатели, ссылки). Например, был const int, а после преобразования стал int или наоборот. Квалификаторы const и volatile называют cv-квалификаторы (cv-qualifiers). Данные квалификаторы указываются перед именами типов. Как ни трудно догадаться квалификатор const задает константность, т.е. защищает переменную от изменения. Квалификатор volatile говорит о том, что значение переменной может меняться без явного выполнения присваивания. Это обеспечивает защиту от оптимизации компилятором операций с данной переменной.

Общий вид приведения:

const_cast<new_type>(exp)

Дополнительный пример от пользователя 5nw

Квалификаторы const и volatile можно удалить или добавить только с помощью оператора приведения const_cast и приведения типов в стиле языка C. Другие операторы приведения типов не влияют на квалификаторы const и volatile (reinterpret_cast, static_cast, dynamic_cast).

reinterpret_cast

Оператор приведения reinterpret_cast используется для приведения несовместимых типов. Может приводить целое число к указателю, указатель к целому числу, указатель к указателю (это же касается и ссылок). Является функционально усеченным аналогом приведения типов в стиле языка С. Отличие состоит в том, что reinterpret_cast не может снимать квалификаторы const и volatile, а также не может делать небезопасное приведение типов не через указатели, а напрямую по значению. Например, переменную типа int к переменной типа double привести при помощи reinterpret_cast нельзя.

Общий вид приведения:

reinterpret_cast<new_type>(exp)

static_cast

Оператор приведения static_cast применяется для неполиморфного приведения типов на этапе компиляции программы. Отличие static_cast от приведения типов в стиле языка C состоит в том, что данный оператор приведения может отслеживать недопустимые преобразования, такие как приведение указателя к значению или наоборот (unsigned int к указателю на double не приведет), а также приведение указателей и ссылок разных типов считается корректным только, если это приведение вверх или вниз по одной иерархии наследования классов, либо это указатель на void. В случае фиксации отклонения от данных ограничений будет выдана ошибка при компиляции программы. При множественном наследовании static_cast может вернуть указатель не на исходный объект, а на его подобъект.

Общий вид приведения:

static _cast<new_type>(exp)

dynamic_cast

Оператор приведения dynamic_cast применяется для полиморфного приведения типов на этапе выполнения программы (класс считается полиморфным, если в нем есть хотя бы одна виртуальная функция). Если указатель, подлежащий приведению, ссылается на объект результирующего класса или объект класса производный от результирующего то приведение считается успешным. То же самое для ссылок. Если приведение невозможно, то на этапе выполнения программы будет возвращен NULL, если приводятся указатели. Если приведение производится над ссылками, то будет сгенерировано исключение std::bad_cast. Несмотря на то, что dynamic_cast предназначен для приведения полиморфных типов по иерархии наследования, он может быть использован и для обычных неполиморфных типов вверх по иерархии. В этом случае ошибка будет получена на этапе компиляции. Оператор приведения dynamic_cast может приводить указатель на полиморфный тип к указателю на void, но не может приводить указатель на void к другому типу. Способность dynamic_cast приводить полиморфные типы обеспечивается системой RTTI (Run-Time Type Identification), которая позволяет идентифицировать тип объекта в процессе выполнения программы. При множественном наследовании dynamic_cast может вернуть указатель не на исходный объект, а на его подобъект.

reinterpret_cast conversion

Converts between types by reinterpreting the underlying bit pattern.

Contents

[edit] Syntax

reinterpret_cast< new-type >( expression )

Returns a value of type new-type .

[edit] Explanation

Unlike static_cast , but like const_cast , the reinterpret_cast expression does not compile to any CPU instructions (except when converting between integers and pointers, or between pointers on obscure architectures where pointer representation depends on its type). It is primarily a compile-time directive which instructs the compiler to treat expression as if it had the type new-type .

Only the following conversions can be done with reinterpret_cast , except when such conversions would cast away constness or volatility.

As with all cast expressions, the result is:

  • an lvalue if new-type is an lvalue reference type or an rvalue reference to function type (since C++11) ;
  • an xvalue if new-type is an rvalue reference to object type;
  • a prvalue otherwise.

[edit] Keywords

[edit] Type aliasing

Whenever an attempt is made to read or modify the stored value of an object of type DynamicType through a glvalue of type AliasedType , the behavior is undefined unless one of the following is true:

  • AliasedType and DynamicType are similar.
  • AliasedType is the (possibly cv-qualified) signed or unsigned variant of DynamicType .
  • AliasedType is std::byte, (since C++17) char , or unsigned char : this permits examination of the object representation of any object as an array of bytes.

Informally, two types are similar if, ignoring top-level cv-qualification:

  • they are the same type; or
  • they are both pointers, and the pointed-to types are similar; or
  • they are both pointers to member of the same class, and the types of the pointed-to members are similar; or
  • they are both arrays of the same size or both arrays of unknown bound, and the array element types are similar.
  • they are both arrays of the same size or at least one of them is array of unknown bound, and the array element types are similar.
  • const int * volatile * and int * * const are similar;
  • const int ( * volatile S :: * const ) [ 20 ] and int ( * const S :: * volatile ) [ 20 ] are similar;
  • int ( * const * ) ( int * ) and int ( * volatile * ) ( int * ) are similar;
  • int ( S :: * ) ( ) const and int ( S :: * ) ( ) are not similar;
  • int ( * ) ( int * ) and int ( * ) ( const int * ) are not similar;
  • const int ( * ) ( int * ) and int ( * ) ( int * ) are not similar;
  • int ( * ) ( int * const ) and int ( * ) ( int * ) are similar (they are the same type);
  • std:: pair < int , int > and std:: pair < const int , int > are not similar.

This rule enables type-based alias analysis, in which a compiler assumes that the value read through a glvalue of one type is not modified by a write to a glvalue of a different type (subject to the exceptions noted above).

Note that many C++ compilers relax this rule, as a non-standard language extension, to allow wrong-type access through the inactive member of a union (such access is not undefined in C).

[edit] Notes

Assuming that alignment requirements are met, a reinterpret_cast does not change the value of a pointer outside of a few limited cases dealing with pointer-interconvertible objects:

Performing a class member access that designates a non-static data member or a non-static member function on a glvalue that does not actually designate an object of the appropriate type — such as one obtained through a reinterpret_cast — results in undefined behavior:

Many compilers issue «strict aliasing» warnings in such cases, even though technically such constructs run afoul of something other than the paragraph commonly known as the «strict aliasing rule».

The purpose of strict aliasing and related rules is to enable type-based alias analysis, which would be decimated if a program can validly create a situation where two pointers to unrelated types (e.g., an int * and a float * ) could simultaneously exist and both can be used to load or store the same memory (see this email on SG12 reflector). Thus, any technique that is seemingly capable of creating such a situation necessarily invokes undefined behavior.

When it is needed to interpret the bytes of an object as a value of a different type, std::memcpy or std::bit_cast (since C++20) can be used:

If the implementation provides std::intptr_t and/or std::uintptr_t , then a cast from a pointer to an object type or cv void to these types is always well-defined. However, this is not guaranteed for a function pointer.

The paragraph defining the strict aliasing rule in the standard used to contain two additional bullets partially inherited from C:

В чём смысл существования reinterpret_cast?

В C++ существует оператор reinterpret_cast , смысл которого заключается в приведении между типами, несовместимыми друг с другом.

Однако подобные преобразования нарушают strict aliasing rule, что провоцирует неопределённое поведение. Те же преобразования, которые этого правила не нарушают, укладываются в const_cast , static_cast и dynamic_cast .

В чём же тогда заключается смысл существования данного оператора, если его использование нарушает стандарт?

Arhadthedev's user avatar

reinterpret_cast используется не только для преобразования указателей одного типа в другой. Существует несколько разных преобразований. cppreference.com выделяет 11 вариантов преобразований:

  1. В свой собственный тип
  2. Указателя в интегральный тип
  3. Интегрального типа в указатель
  4. Типа std::nullptr_t в интегральный тип
  5. Указателя одного типа в указатель другого типа
  6. lvalue одного типа в ссылку на другой тип
  7. Указателя на функцию одного типа в указатель на функцию другого типа
  8. Указателя на функцию в void*
  9. Нулевого указателя любого типа в указатель любого другого типа
  10. rvalue указатель одного типа на функцию-член в указатель другого типа на функцию-член
  11. rvalue указатель члена-данных одного типа в указатель ну другой член-данных другого типа

Type aliasing-правила затрагивают только пункты 5 и 6 и результат может быть безопасно использован (т.е. без нарушения strict-aliasing) в следующих случаях:

  • Результирующий тип есть динамический тип исходного объекта
  • Результирующий тип и динамический тип указывают на одинаковый тип T
  • Результирующий тип есть знаковый или беззнаковый вариант типа исходного объекта
  • Результирующий тип есть агрегатный тип или union , в котором содержится элемент или нестатический член данных, используемый в качестве исходного объекта. Т.е. можно получить указатель на структуру по указателю на её член.
  • Результирующий тип есть базовый класс динамического типа исходного объекта и этот тип является standard-layout классом и не содержит нестатических членов-данных, и результирующий тип — первый базовый класс.
  • Результирующий тип есть указатель на char , unsigned char или std::byte .

Некоторые реализации ослабляют эти правила в качестве нестандартных расширений языка.

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

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