Namespaces
Variants

reinterpret_cast conversion

From cppreference.net
C++ language
General topics
Flow control
Conditional execution statements
Iteration statements (loops)
Jump statements
Functions
Function declaration
Lambda function expression
inline specifier
Dynamic exception specifications ( until C++17* )
noexcept specifier (C++11)
Exceptions
Namespaces
Types
Specifiers
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Storage duration specifiers
Initialization
Expressions
Alternative representations
Literals
Boolean - Integer - Floating-point
Character - String - nullptr (C++11)
User-defined (C++11)
Utilities
Attributes (C++11)
Types
typedef declaration
Type alias declaration (C++11)
Casts
Memory allocation
Classes
Class-specific function properties
Special member functions
Templates
Miscellaneous

通过重新解释底层位模式在类型之间进行转换。

目录

语法

reinterpret_cast< 目标类型 >( 表达式 )

返回类型为 target-type 的值。

说明

static_cast 不同,但类似于 const_cast reinterpret_cast 表达式不会编译为任何 CPU 指令(除了在整数与指针之间转换时,或在某些特殊架构上指针表示依赖于其类型时进行指针转换的情况)。它主要是一个编译时指令,指示编译器将 expression 视为具有 target-type 类型。

只有以下转换可以通过 reinterpret_cast 实现,除非这些转换会 去除常量性 (或易变性)。

1) 整型、枚举类型、指针类型或成员指针类型的表达式可以转换为它自身的类型。转换结果的值与 expression 的值相同。
2) 指针可以转换为任何足以容纳其类型所有值的整型(例如转换为 std::uintptr_t )。
3) 任何整数或枚举类型的值均可转换为指针类型。将指针转换为足够大小的整数并再次转换回相同指针类型时,可保证获得原始值;否则生成的指针无法安全解引用(相反方向的往返转换不保证结果相同;同一指针可能具有多种整数表示形式)。空指针常量 NULL 或整数值零不保证会生成目标类型的空指针值;为此应使用 static_cast 隐式转换
4) 任何 std::nullptr_t 类型的值(包括 nullptr )都可以转换为任何整数类型,转换方式如同 ( void * ) 0 ,但没有任何值(甚至包括 nullptr )能够转换为 std::nullptr_t :为此应当使用 static_cast
(C++11 起)
5) 任何对象指针类型 T1* 均可转换为其他对象指针类型 cv T2* 。这完全等价于 static_cast < cv T2 * > ( static_cast < cv void * > ( expression ) ) (这意味着若 T2 的对齐要求不比 T1 更严格,则指针值不会改变,且将结果指针转换回原始类型将得到原始值)。无论何种情况,仅当解引用的值满足 类型可访问性 时,才能安全地对结果指针进行解引用。
6) 一个类型为 T1 左值 (C++11 前) 泛左值 (C++11 起) 表达式可被转换为对另一类型 T2 的引用。转换结果等同于 * reinterpret_cast < T2 * > ( p ) ,其中 p 是指向 表达式 所指定对象或函数的“指向 T1 的指针”类型指针。 不会具体化或 (C++17 起) 创建临时对象,不会进行复制,不会调用构造函数或转换函数。仅当结果引用满足 类型可访问性 时才能安全访问。
7) 任何指向函数的指针都可以转换为指向不同函数类型的指针。结果未指定,但将这样的指针转换回指向原始函数类型的指针会得到指向原始函数的指针。只有当函数类型与原始函数类型 调用兼容 时,才能安全调用结果指针。
8) 在某些实现中(特别是在任何符合POSIX标准的系统上,如 dlsym 所要求的),函数指针可以转换为 void * 或任何其他对象指针,反之亦然。如果实现支持双向转换,转换回原始类型将得到原始值;否则,生成的指针无法安全地进行解引用或调用。
9) 任何指针类型的空指针值都可以转换为其他指针类型,得到目标类型的空指针值。注意:空指针常量 nullptr 或任何 std::nullptr_t 类型的值都不能通过 reinterpret_cast 转换为指针类型:为此应当使用隐式转换或 static_cast
10) 指向成员函数的指针可以转换为指向不同类型成员函数的指针。转换回原始类型将得到原始值,否则生成的指针无法安全使用。
11) 指向某个类 T1 成员对象的指针可转换为指向另一个类 T2 成员对象的指针。若 T2 的对齐要求不比 T1 更严格,则转换回原始类型 T1 将得到原始值,否则生成的指针无法安全使用。

与所有强制转换表达式一样,结果是:

  • target-type 是左值引用类型 或函数类型的右值引用 (C++11 起) ,则为左值;
  • target-type 是对象类型的右值引用,则为 xvalue;
(since C++11)
  • 否则为纯右值。

类型别名

类型可访问性

若类型 T_ref 与下列任一类型 相似 ,则具有 动态类型 T_obj 的对象可通过类型为 T_ref 左值 (C++11 前) 泛左值 (C++11 起) 进行 类型可访问

  • char , unsigned char std::byte (C++17 起) :允许将任何对象的 对象表示 作为字节数组进行检视。
  • T_obj
  • T_obj 对应的有符号或无符号类型

如果程序试图通过一个类型不可访问的 lvalue (until C++11) glvalue (since C++11) 来读取或修改对象的存储值,其行为是未定义的。

此规则启用基于类型的别名分析,编译器据此假定通过某类型的泛左值读取的值不会被写入不同类型的泛左值所修改(受前述例外情况约束)。

请注意,许多C++编译器会放宽此规则,作为非标准语言扩展,允许通过 联合体 的非活跃成员进行错误类型访问(此类访问在C语言中不属于未定义行为)。

调用兼容性

若满足以下任一条件,则类型 T_call 与函数类型 T_func 调用兼容的

  • T_call T_func 类型相同。
(since C++17)

如果通过表达式调用函数,且该表达式的 函数类型 与被调用函数定义的类型不兼容,则行为是未定义的。

注释

假设对齐要求得到满足, reinterpret_cast 不会改变 指针的值 ,除了少数涉及 指针可互转换 对象的特殊情况:

struct S1 { int a; } s1;
struct S2 { int a; private: int b; } s2; // 非标准布局类型
union U { int a; double b; } u = {0};
int arr[2];
int* p1 = reinterpret_cast<int*>(&s1); // p1 的值为“指向 s1.a 的指针”,因为
                                       // s1.a 与 s1 是指针可互转换的
int* p2 = reinterpret_cast<int*>(&s2); // p2 的值不会被 reinterpret_cast 改变
                                       // 且为“指向 s2 的指针”
int* p3 = reinterpret_cast<int*>(&u);  // p3 的值为“指向 u.a 的指针”:
                                       // u.a 与 u 是指针可互转换的
double* p4 = reinterpret_cast<double*>(p3); // p4 的值为“指向 u.b 的指针”:u.a 与
                                            // u.b 是指针可互转换的,因为
                                            // 两者都与 u 指针可互转换
int* p5 = reinterpret_cast<int*>(&arr); // p5 的值不会被 reinterpret_cast 改变
                                        // 且为“指向 arr 的指针”

对实际上并未指向适当类型对象的泛左值(例如通过 reinterpret_cast 获取的对象)执行指定非静态数据成员或非静态成员函数的类成员访问,将导致未定义行为:

struct S { int x; };
struct T { int x; int f(); };
struct S1 : S {};    // 标准布局类型
struct ST : S, T {}; // 非标准布局类型
S s = {};
auto p = reinterpret_cast<T*>(&s); // p 的值为"指向 s 的指针"
auto i = p->x; // 类成员访问表达式是未定义行为;
               // s 不是 T 类型对象
p->x = 1; // 未定义行为
p->f();   // 未定义行为
S1 s1 = {};
auto p1 = reinterpret_cast<S*>(&s1); // p1 的值为"指向 s1 中 S 子对象的指针"
auto i = p1->x; // 正确
p1->x = 1;      // 正确
ST st = {};
auto p2 = reinterpret_cast<S*>(&st); // p2 的值为"指向 st 的指针"
auto i = p2->x; // 未定义行为
p2->x = 1;      // 未定义行为

许多编译器在此类情况下会发出"严格别名"警告,尽管从技术上讲,这类结构违反的并非通常被称为"严格别名规则"的条款。

严格别名规则及相关规则的目的在于启用基于类型的别名分析。如果程序能够合法地创建这样一种情形:两个指向不相关类型的指针(例如 int * float * )可以同时存在,且两者都能用于对同一内存进行加载或存储操作(参见 SG12邮件组中的这封邮件 ),那么类型别名分析将遭到破坏。因此,任何看似能够创建此类情形的技术都必然引发未定义行为。

当需要将对象的字节解释为不同类型值时,可使用 std::memcpy std::bit_cast (C++20 起)

double d = 0.1;
std::int64_t n;
static_assert(sizeof n == sizeof d);
// n = *reinterpret_cast<std::int64_t*>(&d); // 未定义行为
std::memcpy(&n, &d, sizeof d);               // 正确
n = std::bit_cast<std::int64_t>(d);          // 同样正确

若实现提供 std::intptr_t 和/或 std::uintptr_t ,则从对象类型指针或 cv void 到这些类型的转换始终是良定义的。但函数指针的此类转换不提供此保证。

(since C++11)

在C语言中,聚合对象的复制和赋值是整体访问聚合对象的。但在C++中,这类操作总是通过成员函数调用执行,该函数访问的是各个子对象而非整个对象(对于联合体而言,则是复制对象表示,即通过 unsigned char 实现)。

关键词

reinterpret_cast

示例

演示 reinterpret_cast 的一些用法:

#include <cassert>
#include <cstdint>
#include <iostream>
int f() { return 42; }
int main()
{
    int i = 7;
    // 指针到整数及反向转换
    std::uintptr_t v1 = reinterpret_cast<std::uintptr_t>(&i); // static_cast 会导致错误
    std::cout << "The value of &i is " << std::showbase << std::hex << v1 << '\n';
    int* p1 = reinterpret_cast<int*>(v1);
    assert(p1 == &i);
    // 函数指针到其他类型指针及反向转换
    void(*fp1)() = reinterpret_cast<void(*)()>(f);
    // fp1(); 未定义行为
    int(*fp2)() = reinterpret_cast<int(*)()>(fp1);
    std::cout << std::dec << fp2() << '\n'; // 安全操作
    // 通过指针的类型别名转换
    char* p2 = reinterpret_cast<char*>(&i);
    std::cout << (p2[0] == '\x7' ? "This system is little-endian\n"
                                 : "This system is big-endian\n");
    // 通过引用的类型别名转换
    reinterpret_cast<unsigned int&>(i) = 42;
    std::cout << i << '\n';
    [[maybe_unused]] const int &const_iref = i;
    // int &iref = reinterpret_cast<int&>(
    //     const_iref); // 编译错误 - 无法去除 const 限定
    // 必须使用 const_cast:int &iref = const_cast<int&>(const_iref);
}

可能的输出:

The value of &i is 0x7fff352c3580
42
This system is little-endian
42

缺陷报告

以下行为变更缺陷报告被追溯应用于先前发布的C++标准。

缺陷报告 适用版本 发布时行为 正确行为
CWG 195 C++98 不允许函数指针与对象指针间的转换 改为有条件支持
CWG 658 C++98 指针转换结果未明确
(除转换回原始类型外)
为满足对齐要求的
指针类型提供规范说明
CWG 799 C++98 不明确 reinterpret_cast
可执行哪些恒等转换
予以明确
CWG 1268 C++11 reinterpret_cast 仅能将左值
转换为引用类型
同时允许亡值
CWG 2780 C++98 reinterpret_cast 不能将
函数左值转换为其他引用类型
允许转换
CWG 2939 C++17 reinterpret_cast 可将
纯右值转换为右值引用类型
不允许转换

参考文献

  • C++23 标准 (ISO/IEC 14882:2024):
  • 7.6.1.10 重新解释转型 [expr.reinterpret.cast]
  • C++20 标准 (ISO/IEC 14882:2020):
  • 7.6.1.9 重新解释转型 [expr.reinterpret.cast]
  • C++17 标准 (ISO/IEC 14882:2017):
  • 8.2.10 重新解释转型 [expr.reinterpret.cast]
  • C++14 标准 (ISO/IEC 14882:2014):
  • 5.2.10 重新解释转型 [expr.reinterpret.cast]
  • C++11 标准 (ISO/IEC 14882:2011):
  • 5.2.10 重解释转换 [expr.reinterpret.cast]
  • C++98 标准 (ISO/IEC 14882:1998):
  • 5.2.10 重解释转换 [expr.reinterpret.cast]
  • C++03 标准 (ISO/IEC 14882:2003):
  • 5.2.10 重解释转换 [expr.reinterpret.cast]

参见

const_cast 转换 添加或移除 const 限定符
static_cast 转换 执行基础类型转换
dynamic_cast 转换 执行带检查的多态转换
显式转换 类型间的宽松转换
标准转换 类型间的隐式转换
(C++20)
将类型的对象表示重新解释为另一类型的对象表示
(函数模板)