我对reinterpret_cast与static_cast的适用性有点困惑。根据我所读到的,一般规则是在编译时可以解释类型时使用静态强制转换,因此出现了单词static。这是C++编译器内部使用的用于隐式转换的强制转换。
reinterpret_casts适用于两种情况,将整数类型转换为指针类型,反之亦然,或者将一种指针类型转换为另一种。我得到的一般想法是这是不可移植的,应该避免。
我有点困惑的地方是我需要的一个用法,我从C调用C++,C代码需要保存到C++对象,所以基本上它持有EDCOX1 4。在void *和类类型之间应该使用什么转换?
我见过static_cast和reinterpret_cast的用法吗?虽然从我读到的内容来看,static似乎更好,因为强制转换可以在编译时发生?虽然它说使用reinterpret_cast从一个指针类型转换到另一个指针类型?
- reinterpret_cast不会在运行时发生。它们都是编译时语句。来自en.cppreference.com/w/cpp/language/reinterpret_cast:"与static_cast不同,但与const_cast一样,reinterpret_cast表达式不会编译为任何CPU指令。它纯粹是一个编译器指令,指示编译器将表达式的位序列(对象表示)视为具有新类型的"类型"。
- @在这里,是否可以从*.c和*.cpp文件中添加相关的代码片段?我认为这能提高问题的解释力。
C++标准保证如下:
static_cast使用指向void*的指针保存地址。也就是说,在下面的a、b和c都指向同一个地址:
1 2 3
| int* a = new int();
void* b = static_cast<void*>(a);
int* c = static_cast<int*>(b); |
reinterpret_cast只保证如果您将一个指针强制转换到另一个类型,然后reinterpret_cast返回到原来的类型,就可以得到原来的值。因此,在以下内容中:
1 2 3
| int* a = new int();
void* b = reinterpret_cast<void*>(a);
int* c = reinterpret_cast<int*>(b); |
A和C包含相同的值,但B的值未指定。(实际上,它通常包含与A和C相同的地址,但在标准中没有指定,在具有更复杂内存系统的计算机上可能不正确。)
对于空隙*之间的铸件,应优先选用static_cast。
- 我喜欢"b"未定义的事实。它能阻止你用它做傻事。如果您将某个对象强制转换为另一个指针类型,则会提出问题,并且您不能依赖它这一事实会使您更加小心。如果您使用了上面的static-cast,那么"b"还有什么用呢?
- 我认为,重新解释cast<>保证了相同的位模式。(与指向其他类型的有效指针不同)。
- @martin-reinterpret_cast<>不能保证产生相同的位模式。"RealTytCase<>所执行的映射是实现定义的"(C++ 03 5.3.10)。然而,标准指出,"这并不令人吃惊"。
- 当使用EDCOX1〔3〕时,在C++ 11中EDCOX1〔2〕的值不再是未指定的。在C++ 03中,EDCOX1对EDCOX1,5 5的约束被禁止用EDCOX1×3来完成(虽然编译器没有实现这一点,它是不切实际的,因此被改变为C++ 11)。
- 嗯,关于从void*到void*的重新解释是正确的。我想知道为什么我写这封信时认为这是合法的。
- 如果你把两个演员混合在一起会怎么样?这是未定义的行为吗?stackoverflow.com/questions/16499683/&hellip;
- 这实际上并不能回答"何时使用重新解释"的问题。
- @我认为没有具体说明并不能阻止你做傻事。它只会在你记得它未指明的时候阻止你。巨大的差异。就我个人而言,我不喜欢不详。太多了,记不起来了。
- @Helinwang,这就是为什么在代码中使用reinterpret_cast是一个大红旗,你知道它很危险,它提醒你检查。static_cast不投大红旗,因此更危险,因为你没有被提示去记住你所拥有的东西是多么可怕。
- static_cast拒绝将函数指针强制转换到void*和后面。reinterpret_cast和c型铸造(void*)适用于本案。
当需要reinterpret_cast时,一种情况是与不透明数据类型进行接口。这经常发生在程序员无法控制的供应商API中。下面是一个人为设计的示例,其中供应商提供用于存储和检索任意全局数据的API:
1 2 3 4
| // vendor.hpp
typedef struct _Opaque * VendorGlobalUserData;
void VendorSetUserData(VendorGlobalUserData p);
VendorGlobalUserData VendorGetUserData(); |
要使用这个API,程序员必须将他们的数据强制转换到VendorGlobalUserData,然后再返回。static_cast不起作用,必须使用reinterpret_cast:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| // 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;
} |
下面是示例API的人为实现:
1 2 3 4
| // vendor.cpp
static VendorGlobalUserData g = 0;
void VendorSetUserData(VendorGlobalUserData p) { g = p; }
VendorGlobalUserData VendorGetUserData() { return g; } |
- 是的,这是我能想到的唯一有意义的重新解释的方法。
- 这可能是一个迟来的问题,但为什么供应商API不使用void*来解决这个问题?
- @Xeo不使用void*,因为它们在编译时会丢失(一些)类型检查。
- "不透明"数据类型的一个实际用例是当你想把一个API暴露给C,但是在C++中写实现。ICU是一个图书馆的例子,它在几个地方都能做到这一点。例如,在spoof-checker API中,您处理类型为USpoofChecker*的指针,其中USpoofChecker是一个空结构。但是,在引擎盖下,每当您通过EDCOX1的15度时,它会对内部C++类型进行EDCOX1×3的调用。
- @为什么不向用户公开C结构类型?
- @hsalimi是因为ICU4C在C API上是二进制兼容的;您可以在任何较新的ICU4C库版本中交换,并且您在较旧版本上构建的二进制文件都可以工作。通过公共API公开该结构意味着我们永远无法向该结构添加新字段,因为该结构被编译为客户端的二进制文件。
简短的回答:如果你不知道reinterpret_cast代表什么,不要使用它。如果你将来需要它,你会知道的。
完全回答:
让我们考虑一下基本的数字类型。
例如,当您将int(12)转换为unsigned float (12.0f)时,您的处理器需要调用一些计算,因为这两个数字的位表示形式不同。这就是static_cast的意思。
另一方面,当您调用reinterpret_cast时,CPU不调用任何计算。它只处理内存中的一组位,就像处理另一个类型一样。所以当你用这个关键字把int*转换成float*时,新的值(指针删除后)与数学意义上的旧值无关。
示例:由于一个原因,reinterpret_cast是不可移植的—字节顺序(endianness)。但这往往是令人惊讶的最佳使用理由。让我们想象一下这个例子:您必须从文件中读取二进制32位数字,并且您知道它是big endian。您的代码必须是通用的,并且可以在big-endian(如某些ARM)和little-endian(如x86)系统上正常工作。所以你必须检查字节顺序。it is known on compile time so you can write constexprfunction:You can write a function to achieve this:
1 2 3 4 5
| /*constexpr*/ bool is_little_endian() {
std::uint16_t x=0x0001;
auto p = reinterpret_cast<std::uint8_t*>(&x);
return *p != 0;
} |
说明:内存中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编译器拒绝编译它(正确)。
- 很好的例子!我会用uint16_t的缩写和uint8_t的无符号字符来代替它,以减少对人类的模糊。
- @Janturo_true,我们不能假设<;cod>;shor<;/cod>在内存中占用16位。更正
- 这个例子是错误的。constexpr函数中不允许使用reinterpret_cast
- 首先,这个代码被最新的clang(7.0.0)和gcc(8.2.0)拒绝。不幸的是,我没有发现正式语言的局限性。我能找到的只有social.msdn.microsoft.com/forums/vstudio/en-us/&hellip
- 这里有一个更好的参考(带标准链接):stackoverflow.com/a/26201529/49554n
- 更具体地说,en.cppreference.com/w/cpp/language/constant_expression(item 16)明确指出,在常量表达式中不能使用reinterpret_cast。还可以查看github.com/cplusplus/draft/blob/master/papers/n3797.pdf(5.19常量表达式)第125-126页,其中明确排除了reinterpret-cast。然后7.1.5 constexpr说明符第5项(第146页)*用于非模板、非默认constexpr函数…如果不存在这样的参数值…可能是核心常量表达式(5.19)的计算子表达式,程序格式不正确*
EDCOX1 2含义的含义不是由C++标准定义的。因此,理论上,reinterpret_cast可能会使您的程序崩溃。在实践中,编译器尝试执行您期望的操作,即解释您传递的内容的位,就好像它们是您要强制转换的类型一样。如果你知道你将要使用的编译器对reinterpret_cast做什么,你就可以使用它,但是说它是可移植的,那就大错特错了。
对于您所描述的情况,以及几乎任何您可能认为是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.
因此,对于您的用例,标准化委员会似乎非常清楚地打算让您使用static_cast。
- 你的程序没有完全崩溃。本标准提供了一些关于重新解释铸模的保证。只是不像人们通常期望的那样多。
- 好吧,重新解释的角色本身可能不会崩溃,但它可能会返回一些伪造的结果,当你试图使用它时,可能会导致崩溃。
- 如果你使用得当就不行了。也就是说,从A到B到A的重新解释是完全安全和明确的。但是b的值是未指明的,是的,如果你依赖它,可能会发生不好的事情。但是,只要你按照标准所允许的方式使用它,铸造本身就足够安全了。;)
- Reinterpret_crash<>不会使代码崩溃。除了那几个保证。
- Lol,我怀疑重新解释"崩溃"可能确实会使你的程序崩溃。但重新解释一下,演员不会。
- 讽刺的是,我在我的编译器上尝试过,但不知何故,它拒绝编译reinterpret_crash。编译器的错误不会阻止我崩溃我的重新解释程序。我会尽快报告一个错误!< /反讽>
- @丹参1〔1〕。
reinterpret-cast的一个用法是,如果您想对(ieee 754)浮点数应用位操作。其中一个例子是快速平方根反比技巧:
https://en.wikipedia.org/wiki/fast_-inverse_-square_-root概述_-the-code
它将浮点的二进制表示视为一个整数,将其右移并从常量中减去它,从而将指数减半并求反。在转换回浮点数后,它将进行牛顿-拉斐逊迭代以使此近似更精确:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| 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;
} |
这最初是用C编写的,所以使用C强制转换,但是类似的C++ CAST是重新解释。
- error: invalid cast of an rvalue expression of type 'int64_t {aka long long int}' to type 'double&' reinterpret_cast((reinterpret_cast(d) >> 1) + (1L << 61))-ideone.com/6s4ijc
- 标准规定这是未定义的行为:en.cppreference.com/w/cpp/language/reinterpret_cast(在"类型别名"下)
- @如果我用memcpy代替所有reinterpret_cast,它仍然是ub吗?
- @沙棘:这是标准的UB,但是如果它适用于你的建筑,不要担心。我想,对于任何英特尔体系结构的编译器来说,这个技巧都是可以的。它不能在其他体系结构上按预期工作(甚至崩溃),例如,浮动和长整型可能存储在单独的内存分区中(我不知道任何这样的体系结构,它只是一个参数…)。memcpy肯定会使其合法化。
您可以在编译时使用reinterprete-cast检查继承。看这里:在编译时使用reinterpret-cast检查继承
首先,您有一些特定类型的数据,如int:
1
| int x = 0x7fffffff://==nan in binary representation |
然后要访问与其他类型(如float)相同的变量:你可以决定
1 2 3
| float y = reinterpret_cast<float&>(x);
//this could only be used in cpp, looks like a function with template-parameters |
或
1 2 3
| float y = *(float*)&(x);
//this could be used in c and cpp |
简而言之:这意味着相同的内存被用作不同的类型。所以您可以将浮点数的二进制表示形式转换为int类型,如上面所述。例如,0x8000000为-0(尾数和指数为空,但符号msb为一。这也适用于双打和长双打。
优化:我认为在许多编译器中重新解释RETU CAST会得到优化,而C-CAST是由pointerarithmetic生成的(值必须复制到内存中,因为指针不能指向CPU寄存器)。
注意:在这两种情况下,您都应该在强制转换之前将强制转换值保存在变量中!这个宏可以帮助:
1
| #define asvar(x) ({decltype(x) __tmp__ = (x); __tmp__; }) |
1 2 3 4 5 6
| template <class outType, class inType>
outType safe_cast(inType pointer)
{
void* temp = static_cast<void*>(pointer);
return static_cast<outType>(temp);
} |
我试图得出结论,并使用模板编写了一个简单的安全强制转换。请注意,此解决方案不保证在函数上强制转换指针。
- 什么?何苦?这正是reinterpret_cast在这种情况下已经做的:"对象指针可以显式转换为不同类型的对象指针[72]当对象指针类型的prvalue v转换为对象指针类型"指向cv T的指针"时,结果是static_cast(static_cast(v))"——n3797。
- 至于c++2003标准,我看不出reinterpret_cast是static_cast(static_cast(v))的标准。
- 好吧,是的,但是我不关心13年前的版本,而且大多数编码人员也不应该(很可能)避免它。除非另有规定,答案和评论应真实反映最新的可用标准。恕我直言。不管怎样,我想委员会认为有必要在2003年之后明确地增加这一点。(因为Irc,在C++ 11中是相同的)
- 在以东十一〔七〕以前,是以东十一〔八〕年。大量的项目使用了旧的C++而不是便携式的。有时你必须关心可移植性。例如,您必须在Solaris、AIX、HPUX、Windows上支持相同的代码。在编译器依赖性和可移植性方面,这是很棘手的。因此引入可移植性地狱的一个好例子是在代码中使用reinterpret_cast。
- 再说一次,如果像我一样,你很乐意把自己限制在那些能很好地使用最新和最好版本的语言的平台上,那么你的反对是没有意义的。
快速回答:如果编译,使用static_cast,否则使用reinterpret_cast。
阅读常见问题!在C中保存C++数据可能有风险。
在C++中,指向对象的指针可以被转换为EDCOX1×0,而不需要任何类型的转换。但反过来说,这不是真的。你需要一个static_cast来恢复原始指针。