C++ 何时使用 reinterpret_cast?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/573294/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
When to use reinterpret_cast?
提问by HeretoLearn
I am little confused with the applicability of reinterpret_cast
vs static_cast
. From what I have read the general rules are to use static cast when the types can be interpreted at compile time hence the word static
. This is the cast the C++ compiler uses internally for implicit casts also.
我对reinterpret_cast
vs的适用性有点困惑static_cast
。从我读过的内容来看,一般规则是在编译时可以解释类型时使用静态类型转换,因此使用static
. 这也是 C++ 编译器在内部用于隐式转换的转换。
reinterpret_cast
s are applicable in two scenarios:
reinterpret_cast
s 适用于两种情况:
- convert integer types to pointer types and vice versa
- convert one pointer type to another. The general idea I get is this is unportable and should be avoided.
- 将整数类型转换为指针类型,反之亦然
- 将一种指针类型转换为另一种。我得到的一般想法是这是不可移植的,应该避免。
Where I am a little confused is one usage which I need, I am calling C++ from C and the C code needs to hold on to the C++ object so basically it holds a void*
. What cast should be used to convert between the void *
and the Class type?
我有点困惑的是我需要的一种用法,我从 C 调用 C++ 并且 C 代码需要保留 C++ 对象,所以基本上它包含一个void*
. 应该使用什么void *
类型转换在和 Class 类型之间进行转换?
I have seen usage of both static_cast
and reinterpret_cast
? Though from what I have been reading it appears static
is better as the cast can happen at compile time? Though it says to use reinterpret_cast
to convert from one pointer type to another?
我已经看到了static_cast
和 的用法reinterpret_cast
?尽管从我一直在阅读的内容来看,它似乎static
更好,因为演员可以在编译时发生?虽然它说用于reinterpret_cast
从一种指针类型转换为另一种指针类型?
采纳答案by jalf
The C++ standard guarantees the following:
C++ 标准保证以下内容:
static_cast
ing a pointer to and from void*
preserves the address. That is, in the following, a
, b
and c
all point to the same address:
static_cast
指向和来自的指针void*
保留地址。也就是说,在下面a
,b
并且c
都指向同一个地址:
int* a = new int();
void* b = static_cast<void*>(a);
int* c = static_cast<int*>(b);
reinterpret_cast
only guarantees that if you cast a pointer to a different type, and then reinterpret_cast
it back to the original type, you get the original value. So in the following:
reinterpret_cast
仅保证如果您将指针转换为不同的类型,然后将reinterpret_cast
其恢复为原始类型,您将获得原始值。所以在下面:
int* a = new int();
void* b = reinterpret_cast<void*>(a);
int* c = reinterpret_cast<int*>(b);
a
and c
contain the same value, but the value of b
is unspecified. (in practice it will typically contain the same address as a
and c
, but that's not specified in the standard, and it may not be true on machines with more complex memory systems.)
a
并c
包含相同的值,但未b
指定的值。(实际上,它通常包含与a
and相同的地址c
,但这在标准中没有指定,并且在具有更复杂内存系统的机器上可能不是这样。)
For casting to and from void*
, static_cast
should be preferred.
对于投射到和从void*
,static_cast
应该是首选。
回答by jwfearn
One case when reinterpret_cast
is necessary is when interfacing with opaque data types. This occurs frequently in vendor APIs over which the programmer has no control. Here's a contrived example where a vendor provides an API for storing and retrieving arbitrary global data:
一种reinterpret_cast
必要的情况是与不透明数据类型接口时。这在程序员无法控制的供应商 API 中经常发生。这是一个人为的示例,其中供应商提供用于存储和检索任意全局数据的 API:
// vendor.hpp
typedef struct _Opaque * VendorGlobalUserData;
void VendorSetUserData(VendorGlobalUserData p);
VendorGlobalUserData VendorGetUserData();
To use this API, the programmer must cast their data to VendorGlobalUserData
and back again. static_cast
won't work, one must use reinterpret_cast
:
要使用这个 API,程序员必须将他们的数据VendorGlobalUserData
来回转换。 static_cast
行不通,必须使用reinterpret_cast
:
// main.cpp
#include "vendor.hpp"
#include <iostream>
using namespace std;
struct MyUserData {
MyUserData() : m(42) {}
int m;
};
int main() {
MyUserData u;
// store global data
VendorGlobalUserData d1;
// d1 = &u; // compile error
// d1 = static_cast<VendorGlobalUserData>(&u); // compile error
d1 = reinterpret_cast<VendorGlobalUserData>(&u); // ok
VendorSetUserData(d1);
// do other stuff...
// retrieve global data
VendorGlobalUserData d2 = VendorGetUserData();
MyUserData * p = 0;
// p = d2; // compile error
// p = static_cast<MyUserData *>(d2); // compile error
p = reinterpret_cast<MyUserData *>(d2); // ok
if (p) { cout << p->m << endl; }
return 0;
}
Below is a contrived implementation of the sample API:
下面是示例 API 的人为实现:
// vendor.cpp
static VendorGlobalUserData g = 0;
void VendorSetUserData(VendorGlobalUserData p) { g = p; }
VendorGlobalUserData VendorGetUserData() { return g; }
回答by jaskmar
The short answer:If you don't know what reinterpret_cast
stands for, don't use it. If you will need it in the future, you will know.
简短的回答:如果你不知道什么reinterpret_cast
代表,不要使用它。如果您将来需要它,您就会知道。
Full answer:
完整答案:
Let's consider basic number types.
让我们考虑基本的数字类型。
When you convert for example int(12)
to unsigned float (12.0f)
your processor needs to invoke some calculations as both numbers has different bit representation. This is what static_cast
stands for.
例如int(12)
,当您转换为unsigned float (12.0f)
处理器时,需要调用一些计算,因为这两个数字具有不同的位表示。这就是所static_cast
代表的。
On the other hand, when you call reinterpret_cast
the CPU does not invoke any calculations. It just treats a set of bits in the memory like if it had another type. So when you convert int*
to float*
with this keyword, the new value (after pointer dereferecing) has nothing to do with the old value in mathematical meaning.
另一方面,当你调用reinterpret_cast
CPU 时不会调用任何计算。它只是处理内存中的一组位,就像它有另一种类型一样。因此,当您使用此关键字转换int*
为 tofloat*
时,新值(在指针取消引用后)在数学意义上与旧值无关。
Example:It is true that reinterpret_cast
is not portable because of one reason - byte order (endianness). But this is often surprisingly the best reason to use it. Let's imagine the example: you have to read binary 32bit number from file, and you know it is big endian. Your code has to be generic and works properly on big endian (e.g. some ARM) and little endian (e.g. x86) systems. So you have to check the byte order. It is well-known on compile time so you can write You can write a function to achieve this:constexpr
function:
示例:确实reinterpret_cast
由于一个原因 - 字节顺序(字节序)不可移植。但这通常是使用它的最佳理由。让我们想象一下这个例子:你必须从文件中读取二进制 32 位数字,并且你知道它是大端。您的代码必须是通用的,并且可以在大端(例如某些 ARM)和小端(例如 x86)系统上正常工作。所以你必须检查字节顺序。在编译时众所周知,因此您可以编写您可以编写一个函数来实现此目的:constexpr
函数:
/*constexpr*/ bool is_little_endian() {
std::uint16_t x=0x0001;
auto p = reinterpret_cast<std::uint8_t*>(&x);
return *p != 0;
}
Explanation:the binary representation of x
in memory could be 0000'0000'0000'0001
(big) or 0000'0001'0000'0000
(little endian). After reinterpret-casting the byte under p
pointer could be respectively 0000'0000
or 0000'0001
. If you use static-casting, it will always be 0000'0001
, no matter what endianness is being used.
说明:x
内存中的二进制表示可以是0000'0000'0000'0001
(大)或0000'0001'0000'0000
(小端)。重新解释转换后,p
指针下的字节可以分别为0000'0000
或0000'0001
。如果您使用静态转换,0000'0001
无论使用什么字节序,它都将始终是。
EDIT:
编辑:
In the first version I made example function is_little_endian
to be constexpr
. It compiles fine on the newest gcc (8.3.0) but the standard says it is illegal. The clang compiler refuses to compile it (which is correct).
在第一个版本中,我将示例函数is_little_endian
设为constexpr
. 它在最新的 gcc (8.3.0) 上编译得很好,但标准说它是非法的。clang 编译器拒绝编译它(这是正确的)。
回答by flodin
The meaning of reinterpret_cast
is not defined by the C++ standard. Hence, in theory a reinterpret_cast
could crash your program. In practice compilers try to do what you expect, which is to interpret the bits of what you are passing in as if they were the type you are casting to. If you know what the compilers you are going to use do with reinterpret_cast
you can use it, but to say that it is portablewould be lying.
reinterpret_cast
C++ 标准没有定义的含义。因此,理论上 areinterpret_cast
可能会使您的程序崩溃。在实践中,编译器尝试做你期望的事情,即解释你传入的部分,就好像它们是你要转换的类型一样。如果你知道你将要使用的编译器做什么,reinterpret_cast
你就可以使用它,但说它是可移植的就是撒谎。
For the case you describe, and pretty much any case where you might consider reinterpret_cast
, you can use static_cast
or some other alternative instead. Among other things the standard has this to say about what you can expect of static_cast
(§5.2.9):
对于您描述的情况,以及几乎所有您可能考虑的情况reinterpret_cast
,您可以使用static_cast
或其他一些替代方法。除其他事项外,该标准还说明了您可以期待什么static_cast
(第 5.2.9 节):
An rvalue of type “pointer to cv void” can be explicitly converted to a pointer to object type. A value of type pointer to object converted to “pointer to cv void” and back to the original pointer type will have its original value.
“指向 cv void 的指针”类型的右值可以显式转换为指向对象类型的指针。转换为“指向 cv void 的指针”并返回原始指针类型的对象指针类型值将具有其原始值。
So for your use case, it seems fairly clear that the standardization committee intended for you to use static_cast
.
因此,对于您的用例,标准化委员会打算让您使用static_cast
.
回答by Adam P. Goucher
One use of reinterpret_cast is if you want to apply bitwise operations to (IEEE 754) floats. One example of this was the Fast Inverse Square-Root trick:
reinterpret_cast 的一种用途是将按位运算应用于 (IEEE 754) 浮点数。这方面的一个例子是快速逆平方根技巧:
https://en.wikipedia.org/wiki/Fast_inverse_square_root#Overview_of_the_code
https://en.wikipedia.org/wiki/Fast_inverse_square_root#Overview_of_the_code
It treats the binary representation of the float as an integer, shifts it right and subtracts it from a constant, thereby halving and negating the exponent. After converting back to a float, it's subjected to a Newton-Raphson iteration to make this approximation more exact:
它将浮点数的二进制表示视为整数,将其右移并从常量中减去它,从而将指数减半和求反。转换回浮点数后,它会进行 Newton-Raphson 迭代,以使这种近似更精确:
float Q_rsqrt( float number )
{
long i;
float x2, y;
const float threehalfs = 1.5F;
x2 = number * 0.5F;
y = number;
i = * ( long * ) &y; // evil floating point bit level hacking
i = 0x5f3759df - ( i >> 1 ); // what the deuce?
y = * ( float * ) &i;
y = y * ( threehalfs - ( x2 * y * y ) ); // 1st iteration
// y = y * ( threehalfs - ( x2 * y * y ) ); // 2nd iteration, this can be removed
return y;
}
This was originally written in C, so uses C casts, but the analogous C++ cast is the reinterpret_cast.
这最初是用 C 编写的,所以使用 C 强制转换,但类似的 C++ 强制转换是 reinterpret_cast。
回答by Martin R.
You could use reinterprete_cast to check inheritance at compile time.
Look here:
Using reinterpret_cast to check inheritance at compile time
您可以使用 reinterprete_cast 在编译时检查继承。
看这里:
使用 reinterpret_cast 在编译时检查继承
回答by Sasha Zezulinsky
template <class outType, class inType>
outType safe_cast(inType pointer)
{
void* temp = static_cast<void*>(pointer);
return static_cast<outType>(temp);
}
I tried to conclude and wrote a simple safe cast using templates. Note that this solution doesn't guarantee to cast pointers on a functions.
我试图总结并使用模板编写了一个简单的安全转换。请注意,此解决方案不保证在函数上强制转换指针。
回答by Avi Ginsburg
One reason to use reinterpret_cast
is when a base class does not have a vtable, but a derived class does. In that case, static_cast
and reinterpret_cast
will result in different pointer values (this would be the atypical case mentioned by jalf above). Just as a disclaimer, I'm not stating that this is part of the standard, but the implementation of several widespread compilers.
使用的原因之一reinterpret_cast
是基类没有 vtable,但派生类有。在这种情况下,static_cast
和reinterpret_cast
将导致不同的指针值(这将是由所提到的非典型情况下jalf以上)。作为免责声明,我并不是说这是标准的一部分,而是几个广泛使用的编译器的实现。
As an example, take the code below:
以下面的代码为例:
#include <cstdio>
class A {
public:
int i;
};
class B : public A {
public:
virtual void func() { }
};
int main()
{
B b;
const A* a = static_cast<A*>(&b);
const A* ar = reinterpret_cast<A*>(&b);
printf("&b = %p\n", &b);
printf(" a = %p\n", a);
printf("ar = %p\n", ar);
printf("difference = %ld\n", (long int)(a - ar));
return 0;
}
Which outputs something like:
输出类似于:
&b = 0x7ffe10e68b38
a = 0x7ffe10e68b40
ar = 0x7ffe10e68b38
difference = 2
&b = 0x7ffe10e68b38
a = 0x7ffe10e68b40
ar = 0x7ffe10e68b38
差 = 2
In all the compilers I tried (MSVC 2015&2017, clang 8.0.0, gcc 9.2, icc 19.0.1 - see godbolt for the last 3) the result of the static_cast
differs from that of the reinterpret_cast
by 2 (4 for MSVC). The only compiler to warn about the difference was clang, with:
在我尝试过的所有编译器(MSVC 2015&2017、clang 8.0.0、gcc 9.2、icc 19.0.1 -最后3个参见godbolt)中,结果static_cast
与reinterpret_cast
by 2(MSVC为4 )的结果不同。唯一警告差异的编译器是 clang,有:
17:16: warning: 'reinterpret_cast' from class 'B *' to its base at non-zero offset 'A *' behaves differently from 'static_cast' [-Wreinterpret-base-class]
const A* ar = reinterpret_cast(&b);
^~~~~~~~~~~~~~~~~~~~~~~~
17:16: note: use 'static_cast' to adjust the pointer correctly while upcasting
const A* ar = reinterpret_cast(&b);
^~~~~~~~~~~~~~~~
static_cast
17:16:警告:'reinterpret_cast' 从类 'B *' 到其非零偏移量的基数 'A *' 的行为不同于 'static_cast' [-Wreinterpret-base-class]
const A* ar = reinterpret_cast(&b) ;
^~~~~~~~~~~~~~~~~~~~~~~~~
17:16:注意:在向上转换
const A* ar = reinterpret_cast(&b) 时使用 'static_cast' 正确调整指针;
^~~~~~~~~~~~~~~~
static_cast
One last caveat is that if the base class has no data members (e.g. the int i;
) then clang, gcc, and icc return the same address for reinterpret_cast
as for static_cast
, whereas MSVC still doesn't.
最后一个需要注意的是,如果基类没有任何数据成员(例如int i;
)然后铛,GCC和ICC为返回相同的地址reinterpret_cast
为static_cast
,而MSVC还没有。
回答by TRPh
Here is a variant of Avi Ginsburg's program which clearly illustrates the property of reinterpret_cast
mentioned by Chris Luengo, flodin, and cmdLP: that the compiler treats the pointed-to memory location as if it were an object of the new type:
这是 Avi Ginsburg 程序的一个变体,它清楚地说明了reinterpret_cast
Chris Luengo、flodin 和 cmdLP 提到的属性:编译器将指向的内存位置视为新类型的对象:
#include <iostream>
#include <string>
#include <iomanip>
using namespace std;
class A
{
public:
int i;
};
class B : public A
{
public:
virtual void f() {}
};
int main()
{
string s;
B b;
b.i = 0;
A* as = static_cast<A*>(&b);
A* ar = reinterpret_cast<A*>(&b);
B* c = reinterpret_cast<B*>(ar);
cout << "as->i = " << hex << setfill('0') << as->i << "\n";
cout << "ar->i = " << ar->i << "\n";
cout << "b.i = " << b.i << "\n";
cout << "c->i = " << c->i << "\n";
cout << "\n";
cout << "&(as->i) = " << &(as->i) << "\n";
cout << "&(ar->i) = " << &(ar->i) << "\n";
cout << "&(b.i) = " << &(b.i) << "\n";
cout << "&(c->i) = " << &(c->i) << "\n";
cout << "\n";
cout << "&b = " << &b << "\n";
cout << "as = " << as << "\n";
cout << "ar = " << ar << "\n";
cout << "c = " << c << "\n";
cout << "Press ENTER to exit.\n";
getline(cin,s);
}
Which results in output like this:
结果是这样的输出:
as->i = 0
ar->i = 50ee64
b.i = 0
c->i = 0
&(as->i) = 00EFF978
&(ar->i) = 00EFF974
&(b.i) = 00EFF978
&(c->i) = 00EFF978
&b = 00EFF974
as = 00EFF978
ar = 00EFF974
c = 00EFF974
Press ENTER to exit.
It can be seen that the B object is built in memory as B-specific data first, followed by the embedded A object. The static_cast
correctly returns the address of the embedded A object, and the pointer created by static_cast
correctly gives the value of the data field. The pointer generated by reinterpret_cast
treats b
's memory location as if it were a plain A object, and so when the pointer tries to get the data field it returns some B-specific data as if it were the contents of this field.
可以看出,B对象首先作为B特定数据在内存中构建,然后是嵌入的A对象。的static_cast
正确返回嵌入对象的地址,并且通过所创建的指针static_cast
正确地给出数据字段的值。生成的指针reinterpret_cast
将b
的内存位置视为普通 A 对象,因此当指针尝试获取数据字段时,它返回一些特定于 B 的数据,就好像它是该字段的内容一样。
One use of reinterpret_cast
is to convert a pointer to an unsigned integer (when pointers and unsigned integers are the same size):
一种用途reinterpret_cast
是将指针转换为无符号整数(当指针和无符号整数大小相同时):
int i;
unsigned int u = reinterpret_cast<unsigned int>(&i);
int i;
unsigned int u = reinterpret_cast<unsigned int>(&i);
回答by cmdLP
First you have some data in a specific type like int here:
首先你有一些特定类型的数据,比如这里的 int :
int x = 0x7fffffff://==nan in binary representation
Then you want to access the same variable as an other type like float: You can decide between
然后您想访问与其他类型(如 float)相同的变量:您可以决定
float y = reinterpret_cast<float&>(x);
//this could only be used in cpp, looks like a function with template-parameters
or
或者
float y = *(float*)&(x);
//this could be used in c and cpp
BRIEF: it means that the same memory is used as a different type. So you could convert binary representations of floats as int type like above to floats. 0x80000000 is -0 for example (the mantissa and exponent are null but the sign, the msb, is one. This also works for doubles and long doubles.
简要:这意味着相同的内存被用作不同的类型。因此,您可以将浮点数的二进制表示转换为像上面那样的 int 类型到浮点数。例如,0x80000000 为 -0(尾数和指数为空,但符号 msb 为 1。这也适用于双打和长双打。
OPTIMIZE: I think reinterpret_cast would be optimized in many compilers, while the c-casting is made by pointerarithmetic (the value must be copied to the memory, cause pointers couldn't point to cpu- registers).
优化:我认为 reinterpret_cast 将在许多编译器中进行优化,而 c-casting 是由指针算术进行的(必须将值复制到内存中,因为指针不能指向 CPU 寄存器)。
NOTE: In both cases you should save the casted value in a variable before cast! This macro could help:
注意:在这两种情况下,您都应该在转换之前将转换的值保存在变量中!这个宏可以帮助:
#define asvar(x) ({decltype(x) __tmp__ = (x); __tmp__; })