我对reinterpret_cast
vs 的适用性感到困惑static_cast
.从我读到的一般规则是使用静态转换,当类型可以在编译时解释,因此这个词static
.这是C++编译器内部用于隐式转换的转换.
reinterpret_cast
s适用于两种情况,将整数类型转换为指针类型,反之亦然,或将一种指针类型转换为另一种指针类型.我得到的一般想法是不可移植的,应该避免.
我有点困惑的地方是我需要的一种用法,我从C调用C++并且C代码需要保持C++对象,所以基本上它拥有一个void*
.什么演员应该用于在void *
类型和类型之间进行转换?
我看过两者的用法static_cast
和reinterpret_cast
?虽然从我读过的内容看起来似乎static
更好,因为演员阵容可以在编译时发生?虽然它说用于reinterpret_cast
从一种指针类型转换为另一种指针类型?
jal*_*alf 417
C++标准保证以下内容:
static_cast
指向和void*
保留地址的指针.也就是说,在下面,a,b和c都指向相同的地址:
int* a = new int();
void* b = static_cast<void*>(a);
int* c = static_cast<int*>(b);
Run Code Online (Sandbox Code Playgroud)
a
只保证如果您将指针转换为其他类型,然后b
返回原始类型,您将获得原始值.所以在下面:
int* a = new int();
void* b = reinterpret_cast<void*>(a);
int* c = reinterpret_cast<int*>(b);
Run Code Online (Sandbox Code Playgroud)
a和c包含相同的值,但未指定b的值.(实际上它通常包含与a和c相同的地址,但是标准中没有指定,并且在具有更复杂内存系统的机器上可能不是这样.)
对于往返于*的铸造,c
应该是首选.
jwf*_*arn 152
有reinterpret_cast
必要的一种情况是与不透明数据类型进行交互时.这在程序员无法控制的供应商API中经常发生.这是一个人为的例子,供应商提供了一个用于存储和检索任意全局数据的API:
// vendor.hpp
typedef struct _Opaque * VendorGlobalUserData;
void VendorSetUserData(VendorGlobalUserData p);
VendorGlobalUserData VendorGetUserData();
Run Code Online (Sandbox Code Playgroud)
要使用此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;
}
Run Code Online (Sandbox Code Playgroud)
以下是示例API的设计实现:
// vendor.cpp
static VendorGlobalUserData g = 0;
void VendorSetUserData(VendorGlobalUserData p) { g = p; }
VendorGlobalUserData VendorGetUserData() { return g; }
Run Code Online (Sandbox Code Playgroud)
jas*_*mar 88
简短的回答:
如果您不知道reinterpret_cast
代表什么,请不要使用它.如果您将来需要它,您就会知道.
完整答案:
我们来考虑基本的数字类型.
例如int(12)
,当您转换为unsigned float (12.0f)
处理器时,需要调用某些计算,因为两个数字都有不同的位表示.这就是static_cast
代表.
另一方面,当你调用reinterpret_cast
CPU时不会调用任何计算.它只是处理内存中的一组位,就像它有另一种类型一样.所以,当你转换int*
到float*
与此关键字,新的值(指针dereferecing之后)无关,在数学意义上的旧值.
示例: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;
}
Run Code Online (Sandbox Code Playgroud)
说明:x
内存中的二进制表示可以是0000'0000'0000'0001
(大)或0000'0001'0000'0000
(小端).重新解释后,p
指针下的字节可以分别为0000'0000
或0000'0001
.如果使用静态转换,0000'0001
无论使用什么字节顺序,它都将始终如此.
flo*_*din 18
其含义reinterpret_cast
不是由C++标准定义的.因此,理论上reinterpret_cast
可能会导致程序崩溃.在实践中,编译器会尝试按照您的期望进行操作,即解释您传入的内容,就好像它们是您要投射的类型一样.如果你知道你将要使用的编译器reinterpret_cast
可以使用它,但是说它是可移植的就是撒谎.
对于您描述的情况,以及您可能考虑的任何情况reinterpret_cast
,您可以使用static_cast
或替代其他替代方案.除此之外,标准还有关于你可以期待什么的说法static_cast
(§5.2.9):
"指向cv void的指针"类型的右值可以显式转换为指向对象类型的指针.转换为"指向cv void的指针"并返回原始指针类型的对象的类型指针值将具有其原始值.
因此,对于您的用例,标准化委员会似乎相当清楚,您打算使用它static_cast
.
Ada*_*her 11
reinterpret_cast的一个用途是,如果要将按位运算应用于(IEEE 754)浮点数.其中一个例子是Fast Inverse Square-Root技巧:
https://en.wikipedia.org/wiki/Fast_inverse_square_root#Overview_of_the_code
它将float的二进制表示形式视为整数,将其向右移动并从常量中减去它,从而将指数减半和否定.转换回浮点数后,它会进行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;
}
Run Code Online (Sandbox Code Playgroud)
这最初是用C语言编写的,所以使用C类型转换,但类似的C++转换是reinterpret_cast.
这是 Avi Ginsburg 程序的一个变体,它清楚地说明了 Chris Luengo、flodin 和 cmdLP 提到的属性reinterpret_cast
:编译器将指向的内存位置视为新类型的对象:
#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);
}
Run Code Online (Sandbox Code Playgroud)
其输出结果如下:
#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);
}
Run Code Online (Sandbox Code Playgroud)
可以看出,B对象首先作为B专用数据构建在内存中,其次是嵌入的A对象。正确static_cast
返回嵌入的A对象的地址,并且创建的指针static_cast
正确给出数据字段的值。reinterpret_cast
生成的指针将b
的内存位置视为普通 A 对象,因此当指针尝试获取数据字段时,它会返回一些特定于 B 的数据,就好像它是该字段的内容一样。
一种用途reinterpret_cast
是将指针转换为无符号整数(当指针和无符号整数大小相同时):
int i;
unsigned int u = reinterpret_cast<unsigned int>(&i);
归档时间: |
|
查看次数: |
312344 次 |
最近记录: |