何时使用reinterpret_cast?
我对reinterpret_cast
与static_cast
的适用性有点困惑。 根据我所读到的内容,一般规则是当可以在编译时解释类型时使用静态强制转换,因此使用“静态”一词。 这也是 C++ 编译器在内部用于隐式转换的转换。
reinterpret_cast
适用于两种场景:
- 将整数类型转换为指针类型,反之亦然,
- 将一种指针类型转换为另一种指针类型。 我的总体想法是这是不可移植的,应该避免。
我有点困惑的是我需要的一种用法,我从 C 调用 C++,而 C 代码需要保留 C++ 对象,所以基本上它保存一个 void*
。 应该使用什么类型转换来在 void *
和 Class 类型之间进行转换?
我见过 static_cast
和 reinterpret_cast
的用法? 尽管从我读到的内容来看,静态似乎更好,因为转换可以在编译时发生? 虽然它说使用reinterpret_cast
从一种指针类型转换为另一种指针类型?
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
s are applicable in two scenarios:
- 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?
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?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(11)
C++ 标准保证以下几点:
static_cast
指向和来自void*
的指针会保留地址。 也就是说,下面的a
、b
和c
都指向同一个地址:reinterpret_cast
仅保证如果将指针转换为不同的类型,然后reinterpret_cast
将其返回到原始类型,您将获得原始值。 因此,在以下内容中:a
和c
包含相同的值,但b
的值未指定。 (实际上,它通常包含与a
和c
相同的地址,但这在标准中没有指定,并且在具有更复杂内存系统的机器上可能不是这样.)对于与
void*
之间的转换,应首选static_cast
。The C++ standard guarantees the following:
static_cast
ing a pointer to and fromvoid*
preserves the address. That is, in the following,a
,b
andc
all point to the same address:reinterpret_cast
only guarantees that if you cast a pointer to a different type, and thenreinterpret_cast
it back to the original type, you get the original value. So in the following:a
andc
contain the same value, but the value ofb
is unspecified. (in practice it will typically contain the same address asa
andc
, but that's not specified in the standard, and it may not be true on machines with more complex memory systems.)For casting to and from
void*
,static_cast
should be preferred.需要
reinterpret_cast
的一种情况是与不透明数据类型交互时。 这种情况经常发生在程序员无法控制的供应商 API 中。 下面是一个人为的示例,其中供应商提供了一个用于存储和检索任意全局数据的 API:要使用此 API,程序员必须将其数据转换为
VendorGlobalUserData
并再次返回。static_cast
不起作用,必须使用reinterpret_cast
:下面是示例 API 的一种人为实现:
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:To use this API, the programmer must cast their data to
VendorGlobalUserData
and back again.static_cast
won't work, one must usereinterpret_cast
:Below is a contrived implementation of the sample API:
简短回答:
如果您不知道
reinterpret_cast
代表什么,请不要使用它。 如果您将来需要它,您就会知道。完整答案:
让我们考虑基本的数字类型。
例如,当您将
int(12)
转换为float (12.0f)
时,您的处理器需要调用一些计算,因为这两个数字具有不同的位表示形式。 这就是static_cast
的含义。另一方面,当您调用
reinterpret_cast
时,CPU 不会调用任何计算。 它只是将内存中的一组位视为另一种类型。 因此,当您使用此关键字将int*
转换为float*
时,新值(指针解引用后)与数学意义上的旧值无关(忽略这一事实)读取该值是未定义的行为)。请注意,在reinterprt_cast'ing之后读取或修改值通常是未定义的行为。 大多数情况下,如果你想实现某些数据的位表示,你应该使用
std::byte
的指针或引用(从C++17开始),这几乎总是合法的操作。 其他“安全”类型有char
和unsigned char
,但我想说它不应该在现代 C++ 中用于此目的,如std::byte< /code> 具有更好的语义。
示例: 确实,
reinterpret_cast
不可移植,原因有一个:字节顺序(字节序)。 但这往往是使用它的最佳理由。 让我们想象一下这个例子:你必须从文件中读取二进制 32 位数字,并且你知道它是大端字节序。 您的代码必须是通用的,并且必须能够在大端(例如某些 ARM)和小端(例如 x86)系统上正常工作。 所以你必须检查字节顺序。它在编译时是众所周知的,因此您可以编写您可以编写一个函数来实现此目的:constexpr
函数:说明:
x
在内存中的二进制表示可以是0000'0000'0000'0001
(大)或0000'0001'0000'0000
代码>(小端)。 重新解释转换后,p
指针下的字节可能分别为0000'0000
或0000'0001
。 如果您使用静态转换,则无论使用什么字节序,它都将始终为0000'0001
。编辑:
在第一个版本中,我将示例函数
is_little_endian
设为constexpr
。 它在最新的 gcc (8.3.0) 上编译得很好,但标准说它是非法的。 clang 编译器拒绝编译它(这是正确的)。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.Full answer:
Let's consider basic number types.
When you convert for example
int(12)
tofloat (12.0f)
your processor needs to invoke some calculations as both numbers have different bit representation. This is whatstatic_cast
stands for.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 convertint*
tofloat*
with this keyword, the new value (after pointer dereferecing) has nothing to do with the old value in mathematical meaning (ignoring the fact that it is undefined behavior to read this value).Be aware that reading or modifying values after reinterprt_cast'ing are very often Undefined Behavior. In most cases, you should use pointer or reference to
std::byte
(starting from C++17) if you want to achieve the bit representation of some data, it is almost always a legal operation. Other "safe" types arechar
andunsigned char
, but I would say it shouldn't be used for that purpose in modern C++ asstd::byte
has better semantics.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 has to work 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 writeYou can write a function to achieve this:constexpr
function:Explanation: the binary representation of
x
in memory could be0000'0000'0000'0001
(big) or0000'0001'0000'0000
(little endian). After reinterpret-casting the byte underp
pointer could be respectively0000'0000
or0000'0001
. If you use static-casting, it will always be0000'0001
, no matter what endianness is being used.EDIT:
In the first version I made example function
is_little_endian
to beconstexpr
. 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).C++ 标准未定义
reinterpret_cast
的含义。 因此,理论上,reinterpret_cast 可能会使您的程序崩溃。 实际上,编译器会尝试执行您期望的操作,即解释您传入的内容,就好像它们是您要转换的类型一样。 如果您知道要使用的编译器对reinterpret_cast
做什么,您就可以使用它,但说它是可移植的是在撒谎。对于您描述的情况,以及几乎任何您可能考虑
reinterpret_cast
的情况,您都可以使用static_cast
或其他替代方案。 除其他事项外,该标准还规定了您对static_cast
的期望(第 5.2.9 节):因此,对于您的用例,标准化委员会似乎很清楚地希望您使用
static_cast
。The meaning of
reinterpret_cast
is not defined by the C++ standard. Hence, in theory areinterpret_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 withreinterpret_cast
you can use it, but to say that it is portable would be lying.For the case you describe, and pretty much any case where you might consider
reinterpret_cast
, you can usestatic_cast
or some other alternative instead. Among other things the standard has this to say about what you can expect ofstatic_cast
(§5.2.9):So for your use case, it seems fairly clear that the standardization committee intended for you to use
static_cast
.reinterpret_cast 的一种用途是如果您想对 (IEEE 754) 浮点数应用按位运算。 其中一个例子是快速逆平方根技巧:
https://en.wikipedia.org/ wiki/Fast_inverse_square_root#Overview_of_the_code
它将浮点数的二进制表示形式视为整数,将其右移并从常数中减去它,从而将指数减半并求负。 转换回浮点数后,它会进行 Newton-Raphson 迭代,以使该近似值更加精确:
这最初是用 C 编写的,因此使用 C 强制转换,但类似的 C++ 强制转换是 reinterpret_cast。
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:
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:
This was originally written in C, so uses C casts, but the analogous C++ cast is the reinterpret_cast.
这是 Avi Ginsburg 程序的一个变体,它清楚地说明了 Chris Luengo、flodin 和 cmdLP 提到的
reinterpret_cast
的属性:编译器将指向的内存位置视为对象new type:输出如下:
可以看出,B 对象首先作为 B 特定数据构建在内存中,然后是嵌入的 A 对象。
static_cast
正确返回嵌入的 A 对象的地址,并且static_cast
创建的指针正确给出数据字段的值。 由reinterpret_cast
生成的指针将b
的内存位置视为普通 A 对象,因此当指针尝试获取数据字段时,它会返回一些 B-特定数据就好像它是该字段的内容一样。reinterpret_cast
的一种用途是将指针转换为无符号整数(当指针和无符号整数大小相同时):int i;
unsigned int u = reinterpret_cast(&i);
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:Which results in output like this:
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 bystatic_cast
correctly gives the value of the data field. The pointer generated byreinterpret_cast
treatsb
'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.One use of
reinterpret_cast
is to convert a pointer to an unsigned integer (when pointers and unsigned integers are the same size):int i;
unsigned int u = reinterpret_cast<unsigned int>(&i);
您可以使用reinterprete_cast 在编译时检查继承。
看这里:
使用reinterpret_cast在编译时检查继承
You could use reinterprete_cast to check inheritance at compile time.
Look here:
Using reinterpret_cast to check inheritance at compile time
首先,您有一些特定类型的数据,例如 int:
然后您想要访问与其他类型(例如 float)相同的变量:
您可以在
或
Brief 之间做出决定:这意味着相同的内存被用作不同的类型。 因此,您可以将浮点数的二进制表示形式(如上面的 int 类型)转换为浮点数。 例如,0x80000000 是 -0(尾数和指数为空,但符号 MSB 为 1。这也适用于双精度数和长双精度数。
优化:我认为在许多编译器中,reinterpret_cast 会得到优化,而 c 转换是由pointerarithmetic创建(该值必须复制到内存,因为指针无法指向cpu寄存器)
注意:在这两种情况下,您都应该在转换之前将转换后的值保存在变量中!
First you have some data in a specific type like int here:
Then you want to access the same variable as an other type like float:
You can decide between
or
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.
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).
NOTE: In both cases you should save the casted value in a variable before cast! This macro could help:
我尝试总结并使用模板编写了一个简单的安全转换。
请注意,此解决方案不保证在函数上强制转换指针。
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.
快速回答:如果可以编译,请使用
static_cast
,否则诉诸reinterpret_cast
。Quick answer: use
static_cast
if it compiles, otherwise resort toreinterpret_cast
.阅读常见问题解答! 在 C 中保存 C++ 数据可能存在风险。
在 C++ 中,指向对象的指针无需任何强制转换即可转换为
void *
。 但反之则不然。 您需要一个static_cast
来获取原始指针。Read the FAQ! Holding C++ data in C can be risky.
In C++, a pointer to an object can be converted to
void *
without any casts. But it's not true the other way round. You'd need astatic_cast
to get the original pointer back.