命名空間
變體
動作

reinterpret_cast 轉換

出自 cppreference.com
< cpp‎ | language
 
 
C++ 語言
一般主題
流程控制
條件執行陳述式
if
疊代陳述式 (迴圈)
for
範圍 for (C++11)
跳躍陳述式
函式
函式宣告
Lambda 函式運算式
inline 指定符
動態例外規範 (直到 C++17*)
noexcept 指定符 (C++11)
例外
命名空間
型別
指定符
const/volatile
decltype (C++11)
auto (C++11)
constexpr (C++11)
consteval (C++20)
constinit (C++20)
儲存期指定符
初始化
 
 

透過重新解釋底層的位元模式(bit pattern)在不同類型之間進行轉換。

目錄

[編輯] 語法

reinterpret_cast< target-type >( expression )

回傳一個 目標型別 的值。

[編輯] 說明

static_cast 不同,但與 const_cast 類似,reinterpret_cast 表達式不會編譯為任何 CPU 指令(除非是在整數與指標之間轉換,或是在指標表示法取決於其類型的特殊架構上進行指標間轉換)。它主要是一個編譯時期指令,指示編譯器將 expression 視為具有 target-type 類型。

只有下列轉換可以使用 reinterpret_cast 完成,除非此類轉換會 強制轉換掉 const 屬性(或 volatility)。

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左值 (lvalue)(C++11 前)廣義左值 (glvalue)(自 C++11 起) 表達式可以轉換為對另一種類型 T2 的引用。其結果是 *reinterpret_cast<T2*>(p) 的結果,其中 p 是指向 expression 所指定物件或函式的「指向 T1 的指標」。不會實體化或(自 C++17 起)建立暫存物件,不進行拷貝,也不會呼叫構造函式或轉換函式。只有當結果引用是類型可存取的時,存取該引用才是安全的。
7) 任何函式指標都可以轉換為不同函式類型的指標。結果是未指定的,但將此類指標轉回原始函式類型的指標會得到原始函式的指標。只有當其函式類型與原始函式類型呼叫相容時,才能安全地呼叫結果指標。
8) 在某些實作(特別是 dlsym 所要求的任何 POSIX 相容系統)上,函式指標可以轉換為 void* 或任何其他物件指標,反之亦然。如果實作支持雙向轉換,則轉回原始類型會得到原始值,否則產生的指標無法安全地解引用或呼叫。
9) 任何指標類型的空指標值都可以轉換為任何其他指標類型,產生該類型的空指標值。請注意,空指標常數 nullptr 或任何其他類型為 std::nullptr_t 的值都不能使用 reinterpret_cast 轉換為指標:應使用隱式轉換或 static_cast
10) 成員函式指標可以轉換為不同類型的不同成員函式的指標。轉回原始類型會產生原始值,否則產生的指標無法安全使用。
11) 某些類別 T1 的成員物件指標可以轉換為另一個類別 T2 的另一個成員物件指標。如果 T2 的對齊不比 T1 更嚴格,則轉回原始類型 T1 會產生原始值,否則產生的指標無法安全使用。

與所有轉型運算式一樣,結果為:

  • 目標型別 為左值參考型別或函式型別的右值參考(自 C++11 起),則為左值;
  • 目標型別 為物件型別的右值參考,則為亡值;
(C++11 起)
  • 其餘情況為純右值。

[編輯] 類型別名 (Type aliasing)

[編輯] 類型可存取性

如果類型 T_ref 與下列任何類型相似 (similar),則 動態類型T_obj 的物件可透過類型為 T_ref左值(C++11 前)廣義左值(自 C++11 起) 進行類型可存取

  • char
  • unsigned char
  • std::byte
(自 C++17 起)
  • T_obj
  • 對應於 T_obj 的有符號或無符號類型

如果程式嘗試透過一個不具備類型可存取性的 左值(C++11 前)廣義左值(自 C++11 起) 來讀取或修改物件的儲存值,則其行為是未定義的 (Undefined Behavior)。

這條規則啟用了基於類型的別名分析 (type-based alias analysis),其中編譯器假設透過一種類型的廣義左值讀取的值不會被對另一種類型的廣義左值的寫入操作所修改(受上述例外情況限制)。

請注意,許多 C++ 編譯器放寬了這條規則,作為非標準的語言擴展,以允許透過 union 的非活躍成員進行錯誤類型的存取(此類存取在 C 語言中不是未定義的)。

[編輯] 呼叫相容性

如果滿足以下任一條件,則類型 T_call 與函式類型 T_func呼叫相容

  • T_callT_func 為同一類型。
(自 C++17 起)

如果透過一個函式類型與被呼叫函式定義的類型不相容的表達式來呼叫該函式,則其行為是未定義的。

[編輯] 註解

假設滿足對齊要求,除了一些涉及 指標可互換 物件的有限情況外,reinterpret_cast 不會改變 指標的值

struct S1 { int a; } s1;
struct S2 { int a; private: int b; } s2; // not standard-layout
union U { int a; double b; } u = {0};
int arr[2];
 
int* p1 = reinterpret_cast<int*>(&s1); // value of p1 is "pointer to s1.a" because
                                       // s1.a and s1 are pointer-interconvertible
 
int* p2 = reinterpret_cast<int*>(&s2); // value of p2 is unchanged by reinterpret_cast
                                       // and is "pointer to s2". 
 
int* p3 = reinterpret_cast<int*>(&u);  // value of p3 is "pointer to u.a":
                                       // u.a and u are pointer-interconvertible
 
double* p4 = reinterpret_cast<double*>(p3); // value of p4 is "pointer to u.b": u.a and
                                            // u.b are pointer-interconvertible because
                                            // both are pointer-interconvertible with u
 
int* p5 = reinterpret_cast<int*>(&arr); // value of p5 is unchanged by reinterpret_cast
                                        // and is "pointer to arr"

在不實際指定適當類型物件的廣義左值(例如透過 reinterpret_cast 獲得的廣義左值)上進行指定非靜態資料成員或非靜態成員函式的類別成員存取,會導致未定義行為。

struct S { int x; };
struct T { int x; int f(); };
struct S1 : S {};    // standard-layout
struct ST : S, T {}; // not standard-layout
 
S s = {};
auto p = reinterpret_cast<T*>(&s); // value of p is "pointer to s"
auto i = p->x; // class member access expression is undefined behavior;
               // s is not a T object
p->x = 1; // undefined behavior
p->f();   // undefined behavior
 
S1 s1 = {};
auto p1 = reinterpret_cast<S*>(&s1); // value of p1 is "pointer to the S subobject of s1"
auto i = p1->x; // OK
p1->x = 1;      // OK
 
ST st = {};
auto p2 = reinterpret_cast<S*>(&st); // value of p2 is "pointer to st"
auto i = p2->x; // undefined behavior
p2->x = 1;      // undefined behavior

許多編譯器在此類情況下會發出「嚴格別名 (strict aliasing)」警告,儘管從技術上講,此類構造違反的是通常被稱為「嚴格別名規則」以外的條款。

嚴格別名及相關規則的目的是實現基於類型的別名分析,如果程式能有效地創立兩個指向無關類型指標(例如 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); // Undefined behavior
std::memcpy(&n, &d, sizeof d);               // OK
n = std::bit_cast<std::int64_t>(d);          // also OK

如果實作提供了 std::intptr_t 和/或 std::uintptr_t,則從物件指標類型或 cv void 到這些類型的強制轉換始終是定義良好的。然而,對於函式指標,這一點並不保證。

(C++11 起)

在 C 語言中,聚合拷貝和賦值會將聚合物件作為一個整體存取。但在 C++ 中,此類動作總是透過成員函式呼叫執行,存取個別子物件而非整個物件(或者在 union 的情況下,拷貝物件表示,即透過 unsigned char)。

[編輯] 關鍵字

reinterpret_cast

[編輯] 範例

展示 reinterpret_cast 的一些用法

#include <cassert>
#include <cstdint>
#include <iostream>
 
int f() { return 42; }
 
int main()
{
    int i = 7;
 
    // pointer to integer and back
    std::uintptr_t v1 = reinterpret_cast<std::uintptr_t>(&i); // static_cast is an error
    std::cout << "The value of &i is " << std::showbase << std::hex << v1 << '\n';
    int* p1 = reinterpret_cast<int*>(v1);
    assert(p1 == &i);
 
    // pointer to function to another and back
    void(*fp1)() = reinterpret_cast<void(*)()>(f);
    // fp1(); undefined behavior
    int(*fp2)() = reinterpret_cast<int(*)()>(fp1);
    std::cout << std::dec << fp2() << '\n'; // safe
 
    // type aliasing through pointer
    char* p2 = reinterpret_cast<char*>(&i);
    std::cout << (p2[0] == '\x7' ? "This system is little-endian\n"
                                 : "This system is big-endian\n");
 
    // type aliasing through reference
    reinterpret_cast<unsigned int&>(i) = 42;
    std::cout << i << '\n';
 
    [[maybe_unused]] const int &const_iref = i;
    // int &iref = reinterpret_cast<int&>(
    //     const_iref); // compiler error - can't get rid of const
    // Must use const_cast instead: int &iref = const_cast<int&>(const_iref);
}

可能輸出

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

[編輯] 缺陷報告

下列更改行為的缺陷報告追溯應用於之前的 C++ 標準。

DR 應用於 出版時的行為 正確的行為
CWG 195 C++98 不允許在函式指標
與物件指標之間進行轉換
改為條件支援
CWG 658 C++98 指標轉換的結果曾是未指定的
(轉回原始類型的情況除外)
為指向類型滿足
對齊要求的
指標提供了規範
CWG 799 C++98 不清楚哪些同一性轉換 (identity conversion)
可以由 reinterpret_cast 完成
現已明確化
CWG 1268 C++11 reinterpret_cast 曾只能轉換
左值到引用類型
現在也允許 xvalue
CWG 2780 C++98 reinterpret_cast 曾不能轉換
函式左值到其他引用類型
已允許
CWG 2939 C++17 reinterpret_cast 曾可以轉換
prvalue 到右值引用類型
不允許

[編輯] 參考文獻

  • C++23 標準 (ISO/IEC 14882:2024)
  • 7.6.1.10 Reinterpret cast [expr.reinterpret.cast]
  • C++20 標準 (ISO/IEC 14882:2020)
  • 7.6.1.9 Reinterpret cast [expr.reinterpret.cast]
  • C++17 標準 (ISO/IEC 14882:2017)
  • 8.2.10 Reinterpret cast [expr.reinterpret.cast]
  • C++14 標準 (ISO/IEC 14882:2014)
  • 5.2.10 Reinterpret cast [expr.reinterpret.cast]
  • C++11 標準 (ISO/IEC 14882:2011)
  • 5.2.10 Reinterpret cast [expr.reinterpret.cast]
  • C++98 標準 (ISO/IEC 14882:1998)
  • 5.2.10 Reinterpret cast [expr.reinterpret.cast]
  • C++03 標準 (ISO/IEC 14882:2003)
  • 5.2.10 Reinterpret cast [expr.reinterpret.cast]

[編輯] 參閱

const_cast 轉換 新增或移除 const[編輯]
static_cast 轉換 執行基本轉換[編輯]
dynamic_cast 轉換 執行受檢查的多型轉換[編輯]
顯式強制轉換 類型間的寬容轉換 [編輯]
標準轉換 從一種類型到另一種類型的隱式轉換[編輯]
(C++20)
將一種型別的物件表示重新解釋為另一種型別
(函式模板) [編輯]
English Deutsch 日本語 한국어 中文(简体) 中文(繁體)