簡體   English   中英

為什么這個 reinterpret_cast 不能編譯?

[英]Why doesn't this reinterpret_cast compile?

我知道reinterpret_cast很危險,我只是為了測試它。 我有以下代碼:

int x = 0;
double y = reinterpret_cast<double>(x);

當我嘗試編譯程序時,它給了我一個錯誤提示

從“float”類型到“double”類型的無效轉換

這是怎么回事? 我認為reinterpret_cast是你可以用來將蘋果轉換為潛艇的流氓演員,為什么這個簡單的演員不能編譯?

在 C++ 中reinterpret_cast只能執行一組特定的轉換,在語言規范中明確列出。 簡而言之, reinterpret_cast只能執行指針到指針的轉換和引用到引用的轉換(加上指針到整數和整數到指針的轉換)。 這與演員表的名稱所表達的意圖是一致的:它旨在用於指針/引用重新解釋。

你要做的不是重新解釋。 如果要將int重新解釋為double ,則必須將其轉換為引用類型

double y = reinterpret_cast<double&>(x); 

盡管等效的基於指針的重新解釋可能更明確

double y = *reinterpret_cast<double*>(&x); // same as above

但請注意,雖然reinterpret_cast可以轉換引用/指針類型,但通過結果引用/指針讀取數據的實際嘗試會產生未定義的行為。

在任何情況下,這當然在具有不同大小的intdouble的平台上沒有多大意義(因為在較大的double情況下,您將讀取超出x占用的內存)。

所以,最終這一切都歸結為你想要實現的目標。 記憶重新詮釋? 看上面。 某種更有意義的intdouble轉換? 如果是這樣, reinterpret_cast不會在這里幫助你。

或許reinterpret_cast更好的思考方式是 rouge 運算符,它可以將指向蘋果的指針“轉換”為指向潛艇的指針

通過將 y 分配給轉換返回的值,您實際上並沒有轉換值x ,而是在轉換它。 也就是說, y不指向x並假裝它指向一個浮點數。 Conversion 構造一個float類型的新值,並將x的值賦給它。 在 C++ 中有幾種方法可以進行這種轉換,其中包括:

int main()
{
    int x = 42;
    float f = static_cast<float>(x);
    float f2 = (float)x;
    float f3 = float(x);
    float f4 = x;
    return 0;
}

唯一真正的區別是最后一個(隱式轉換)將生成更高警告級別的編譯器診斷。 但它們在功能上都做同樣的事情——在許多情況下實際上是同樣的事情,就像在相同的機器代碼中一樣。

現在,如果你真的想假裝x是一個浮點數,那么你真的想通過這樣做來轉換x

#include <iostream>
using namespace std;

int main()
{
    int x = 42;
    float* pf = reinterpret_cast<float*>(&x);
    (*pf)++;
    cout << *pf;
    return 0;
}

可見這有多危險。 事實上,當我在我的機器上運行它時的輸出是1 ,這絕對不是 42+1。

如果您嘗試將int的位轉換為double的表示形式,則需要轉換地址而不是值。 您還必須確保尺寸匹配:

uint64_t x = 0x4045000000000000;
double y = *reinterpret_cast<double *>(&x);

reinterpret_cast 不是一般的演員表。 根據 C++03 規范第 5.2.10.1 節:

下面列出了可以使用 reinterpret_cast 顯式執行的轉換。 不能使用 reinterpret_cast 顯式執行其他轉換。

並且沒有列出任何描述整數和浮點類型之間(或整數類型之間,即使這是非法的reinterpret_cast<long>(int(3)); )的轉換

編譯器拒絕你寫的廢話,因為intdouble可能是大小不同的對象。 您可以通過這種方式實現相同的效果,盡管這肯定是危險的:

int x = 0;
double y = *reinterpret_cast<double*>(&x);

這是潛在的危險,因為如果xy的大小不同(假設int是四個字節, double是八個字節),那么當您取消引用&x處的八個內存字節以填充y您將訪問四個字節的x和四個字節of ... 內存中接下來出現的任何內容(可能是y的開始,或垃圾,或其他完全不同的東西。)

如果要將整數轉換為雙static_cast數,請使用static_cast並執行轉換。

如果要訪問x的位模式,請轉換為一些方便的指針類型(例如byte* )並訪問sizeof(int) / sizeof(byte)

byte* p = reinterpret_cast<byte*>(&x);
for (size_t i = 0; i < sizeof(int); i++) {
  // do something with p[i]
}

重新解釋強制轉換允許您將內存塊重新解釋為不同類型。 這必須在指針或引用上執行:

int x = 1;
float & f = reinterpret_cast<float&>(x);
assert( static_cast<float>(x) != f );   // !!

另一件事是,它實際上是一個非常危險的轉換,不僅是因為作為結果出現奇怪的值,或者上面的斷言沒有失敗,而且因為如果類型的大小不同,並且您從“源”重新解釋為'destination' 類型,對重新解釋的引用/指針的任何操作都將訪問sizeof(destination)字節。 如果sizeof(destination)>sizeof(source)那么這將超出實際變量內存,可能會殺死您的應用程序或覆蓋源或目標以外的其他變量:

struct test {
   int x;
   int y;
};
test t = { 10, 20 };
double & d = reinterpret_cast<double&>( t.x );
d = 1.0/3.0;
assert( t.x != 10 ); // most probably at least.
assert( t.y != 20 );

reinterpret_cast最適合用於指針。 因此,指向一個對象的指針可以變成“潛艇”。

msdn

reinterpret_cast 運算符可用於諸如 char* 到 int* 或 One_class* 到 Unrelated_class* 之類的轉換,這些本質上是不安全的。

reinterpret_cast 的結果不能安全地用於除轉換回其原始類型之外的任何其他事情。 其他用途充其量是不可移植的。

重新解釋的方法使我走上了一條奇怪的道路,結果不一致。 最后我發現像這樣 memcpy 更好!

double source = 0.0;
uint64_t dest;
memcpy(&dest, &source, sizeof dest);

將 int 轉換為 double 不需要轉換。 編譯器將隱式執行賦值。

reinterpret_cast 與指針和引用一起使用,例如,將int *轉換為double *

那很有意思。 也許它在嘗試將強制轉換加倍之前進行了從 int 到 float 的隱式轉換。 int 和 float 類型的字節大小往往相同(當然取決於您的系統)。

使用聯合。 這是在整數和浮點類型之間進行內存映射的最不容易出錯的方法。 重新解釋指針將導致別名警告。

#include <stdio.h>
#include <stdint.h>

int main(int argc, char *argv[])
{
    union { uint32_t i; float f; } v;  // avoid aliasing rules trouble
    v.i = 42;
    printf("int 42 is float %f\n", v.f);
    v.f = 42.0;
    printf("float 42 is int 0x%08x\n", v.i);
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM