名稱空間
變體
操作

reinterpret_cast 轉換

來自 cppreference.com
< cpp‎ | 語言
 
 
C++ 語言
 
 

透過重新解釋底層位模式在型別之間進行轉換。

目錄

[編輯] 語法

reinterpret_cast< 目標型別 >( 表示式 )

返回 目標型別 的值。

[編輯] 解釋

static_cast 不同,但與 const_cast 類似,reinterpret_cast 表示式不會編譯成任何 CPU 指令(除非在整數和指標之間轉換,或者在某些指標表示取決於其型別的晦澀架構上轉換指標)。它主要是一個編譯時指令,指示編譯器將 表示式 視為具有 目標型別

只有以下轉換可以使用 reinterpret_cast 完成,除非此類轉換會去除 constness(或 volatility)。

1) 整型、列舉、指標或成員指標型別的表示式可以轉換為其自身型別。結果值與 表示式 的值相同。
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*>(表示式))(這意味著如果 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 會產生原始值,否則結果指標不能安全使用。

與所有轉換表示式一樣,結果是

  • 如果 目標型別 是左值引用型別或函式型別的右值引用(C++11 起),則為左值;
  • 如果 目標型別 是物件型別的右值引用,則為將亡值;
(C++11 起)
  • 否則為純右值。

[編輯] 類型別名

[編輯] 型別可訪問性

如果型別 T_ref 與以下任何型別相似,則透過型別為 T_ref左值(C++11前)泛左值(C++11起)可以型別可訪問動態型別T_obj 的物件:

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

如果程式試圖透過其不可型別訪問的左值(C++11前)泛左值(C++11起)讀取或修改物件的儲存值,則行為未定義。

此規則支援基於型別的別名分析,其中編譯器假設透過一種型別的泛左值讀取的值不會被寫入不同型別的泛左值所修改(受上述例外情況的約束)。

請注意,許多 C++ 編譯器放寬了此規則,作為非標準語言擴充套件,允許透過 聯合體 的非活動成員進行錯誤型別訪問(此類訪問在 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

許多編譯器在這種情況下會發出“嚴格別名”警告,儘管從技術上講,此類構造與通常稱為“嚴格別名規則”的段落之外的其他內容相悖。

嚴格別名和相關規則的目的是啟用基於型別的別名分析,如果程式可以有效地建立兩個不相關型別的指標(例如,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++ 中,此類操作始終透過成員函式呼叫執行,該呼叫訪問各個子物件而不是整個物件(或者,對於聯合體,複製物件表示,即透過 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++ 標準。

缺陷報告 應用於 釋出時的行為 正確的行為
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 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)
將一種型別的物件表示重新解釋為另一種型別的物件表示
(函式模板) [編輯]