![](/img/trans.png)
[英]Why doesn't reinterpret_cast convert 'unsigned char' to 'char'?
[英]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
可以轉換引用/指針類型,但通過結果引用/指針讀取數據的實際嘗試會產生未定義的行為。
在任何情況下,這當然在具有不同大小的int
和double
的平台上沒有多大意義(因為在較大的double
情況下,您將讀取超出x
占用的內存)。
所以,最終這一切都歸結為你想要實現的目標。 記憶重新詮釋? 看上面。 某種更有意義的int
到double
轉換? 如果是這樣, 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));
)的轉換
編譯器拒絕你寫的廢話,因為int
和double
可能是大小不同的對象。 您可以通過這種方式實現相同的效果,盡管這肯定是危險的:
int x = 0;
double y = *reinterpret_cast<double*>(&x);
這是潛在的危險,因為如果x
和y
的大小不同(假設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.