Reinterpret_cast与c风格的强制转换
Reinterpret_cast vs. C-style cast
我听说reinterpret_cast
是实现定义的,但我不知道这到底是什么意思。你能提供一个例子说明它是如何出错的吗?如果出错了,使用c风格强制转换是不是更好?
c风格的强制转换并不好。
它只是按顺序尝试各种c++风格的强制转换,直到找到一个有效的。这意味着当它像reinterpret_cast
时,它和reinterpret_cast
有同样的问题。但除此之外,它还存在这些问题:
- 它可以做许多不同的事情,从阅读代码中并不总是清楚将调用哪种类型的强制转换(它可能表现得像
reinterpret_cast
,const_cast
或static_cast
,而那些做非常不同的事情) 因此,改变周围的代码可能会改变强制转换的行为 - 在阅读或搜索代码时很难找到-
reinterpret_cast
很容易找到,这很好,因为强制转换很难看,在使用时应该注意。相反,c风格的强制转换(如(int)42.0
)很难通过搜索 来可靠地找到。
回答你问题的另一部分,是的,reinterpret_cast
是实现定义的。这意味着,当您使用它从int*
转换为float*
时,您不能保证结果指针指向相同的地址。这部分是由实现定义的。但是,如果您将结果float*
和reinterpret_cast
返回到int*
中,那么您将获得原始指针。那部分是有保证的。
但是,再次记住,无论你使用reinterpret_cast
还是c风格的强制转换,这都是正确的:
int i;
int* p0 = &i;
float* p1 = (float*)p0; // implementation-defined result
float* p2 = reinterpret_cast<float*>(p0); // implementation-defined result
int* p3 = (int*)p1; // guaranteed that p3 == p0
int* p4 = (int*)p2; // guaranteed that p4 == p0
int* p5 = reinterpret_cast<int*>(p1); // guaranteed that p5 == p0
int* p6 = reinterpret_cast<int*>(p2); // guaranteed that p6 == p0
它在某种意义上是实现定义的,标准(几乎)没有规定不同类型的值在位级别上应该是什么样子,地址空间应该如何结构等等。这是一个平台特定的转换比如:
double d;
int &i = reinterpret_cast<int&>(d);
但是按照标准
对于那些知道寻址结构的人来说,这是意料之中的
所以,如果你知道你在做什么,以及它在底层的样子,就不会出错。
C风格的强制转换在某种意义上有点类似,它可以执行reinterpret_cast,但它也会先"尝试"static_cast,并且可以放弃cv限制(而static_cast和reinterpret_cast不能)并执行忽略访问控制的转换(参见c++ 11标准中的5.4/4)。例如:
#include <iostream>
using namespace std;
class A { int x; };
class B { int y; };
class C : A, B { int z; };
int main()
{
C c;
// just type pun the pointer to c, pointer value will remain the same
// only it's type is different.
B *b1 = reinterpret_cast<B *>(&c);
// perform the conversion with a semantic of static_cast<B*>(&c), disregarding
// that B is an unaccessible base of C, resulting pointer will point
// to the B sub-object in c.
B *b2 = (B*)(&c);
cout << "reinterpret_cast:t" << b1 << "n";
cout << "C-style cast:tt" << b2 << "n";
cout << "no cast:tt" << &c << "n";
}
,下面是ideone的输出:
<>之前reinterpret_cast: 0 xbfd84e78C-style cast: 0xbfd84e7c无强制转换:0xbfd84e78之前注意reinterpret_cast产生的值与'c'的地址完全相同,而c风格的强制转换会产生一个正确偏移的指针。
使用reinterpret_cast
是有正当理由的,由于这些原因,标准实际上定义了发生的事情。
第一种是使用不透明的指针类型,要么用于库API,要么只是将各种指针存储在单个数组中(显然与其类型一起)。你可以将一个指针转换为一个合适大小的整数,然后再转换回一个指针,它将是完全相同的指针。例如:
T b;
intptr_t a = reinterpret_cast<intptr_t>( &b );
T * c = reinterpret_cast<T*>(a);
在这段代码中,保证c
指向对象b
。转换回不同的指针类型当然是未定义的(在某种程度上)。
对于函数指针和成员函数指针也允许类似的转换,但在后一种情况下,您可以将其转换为另一个成员函数指针,只是为了有一个足够大的变量。
第二种情况是使用标准布局类型。这在c++ 11之前就得到了实际支持,现在已经在标准中指定了。在这种情况下,标准首先将reinterpret_cast作为static_cast转换为void*,然后再将其转换为目标类型。这在二进制协议中经常使用,其中数据结构通常具有相同的头信息,并允许您转换具有相同布局但在c++类结构中不同的类型。
在这两种情况下,您都应该使用显式的reinterpret_cast
操作符,而不是C-Style。虽然c风格通常会做同样的事情,但它有受到重载转换操作符影响的危险。
c++有类型,通常它们之间相互转换的唯一方法是通过您编写的定义良好的转换操作符。一般来说,这就是你编写程序所需要和应该使用的全部内容。
然而,有时候,您希望将表示类型的位重新解释为其他内容。这通常用于非常低级的操作,通常不应该使用。对于这些情况,您可以使用reinterpret_cast
。
它是实现定义的,因为c++标准并没有真正说明应该如何在内存中布局。这是由特定的c++实现控制的。因此,reinterpret_cast
的行为取决于编译器如何在内存中布局结构以及如何实现reinterpret_cast
。
c风格的强制转换与reinterpret_cast
非常相似,但它们的语法要少得多,不推荐使用。这种想法认为,强制转换本质上是一个丑陋的操作,它需要丑陋的语法来通知程序员发生了可疑的事情。
一个容易出错的例子:
std::string a;
double* b;
b = reinterpret_cast<double*>(&a);
*b = 3.4;
这个程序的行为是未定义的——编译器可以对它做任何它喜欢的事情。最有可能的是,当调用string
的析构函数时,您会遇到崩溃,但是谁知道呢!它可能只会破坏堆栈并导致不相关函数的崩溃。
reinterpret_cast
和c风格的强制转换都是实现定义的,它们几乎做同样的事情。它们的区别是:
1. reinterpret_cast
无法移除constness。例如:
const unsigned int d = 5;
int *g=reinterpret_cast< int* >( &d );
将发出错误:
error: reinterpret_cast from type 'const unsigned int*' to type 'int*' casts away qualifiers
2。如果您使用reinterpret_cast
,很容易找到您做过的地方。不可能与c风格的投
c风格的投有时type-pun对象在一个未指明的方式,如(unsigned int)-1
,有时相同的值转换成不同的格式,如(double)42
,有时可以做,比如(void*)0xDEADBEEF
再认识位但(void*)0
保证是一个空指针常量,也不一定有相同的对象表示(intptr_t)0
,很少告诉编译器做shoot_self_in_foot_with((char*)&const_object);
。
这通常都很好,但是当你想要将double
转换为uint64_t
时,有时你想要值,有时你想要位。如果你懂C,你就知道C风格的强制转换做的是哪一种,但在某些方面,对这两种类型使用不同的语法会更好。
Bjarne Stroustrup,在他的指导方针中,在另一种情况下推荐reinterpret_cast
:如果你想以一种语言没有由static_cast
定义的方式输入双关,他建议你使用reinterpret_cast<double&>(uint64)
这样的方法而不是其他方法。它们都是未定义的行为,但这使得你在做什么很明显,你是故意这样做的。
- cv::Mat灰色风格转换
- 将 GCC/ATT 风格的汇编器转换为可视化工作室汇编器
- 在没有c风格强制转换的情况下,将DWORD_PTR强制转换为class,反之亦然
- 我应该使用c++的reinterpret_cast而不是C风格的强制转换吗?
- 使用c++风格的类型转换将int转换为char
- c++中的构造函数调用或函数风格强制转换
- 将任何容器转换为c风格的数组视图
- Reinterpret_cast与c风格的强制转换
- 旧风格使用sys/select.h宏强制转换警告
- c++枚举类型可以作为函数调用吗?或者它只是一种不同风格的类型转换
- 将字符串转换为c风格字符串并检测空终止字符
- 难以理解c风格的类型转换和动态转换
- c++中的C风格强制转换会产生奇怪的行为
- 在哪些情况下,基类到派生类的c风格强制转换可能导致崩溃
- 通过c风格转换将char*转换为QString
- c++规范是否说明如何在static_cast/const_cast链中选择用于C风格强制转换的类型?
- 转换需要reinterpret_cast、c风格强制转换或函数风格强制转换
- 将c风格的程序转换为c++
- c++类型转换,用于C风格的向下转换
- 编译器开关在c风格的强制转换中禁用const_cast语义