Reinterpret_cast与c风格的强制转换

Reinterpret_cast vs. C-style cast

本文关键字:转换 风格 cast Reinterpret      更新时间:2023-10-16

我听说reinterpret_cast是实现定义的,但我不知道这到底是什么意思。你能提供一个例子说明它是如何出错的吗?如果出错了,使用c风格强制转换是不是更好?

c风格的强制转换并不好。

它只是按顺序尝试各种c++风格的强制转换,直到找到一个有效的。这意味着当它像reinterpret_cast时,它和reinterpret_cast有同样的问题。但除此之外,它还存在这些问题:

  • 它可以做许多不同的事情,从阅读代码中并不总是清楚将调用哪种类型的强制转换(它可能表现得像reinterpret_cast, const_caststatic_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)这样的方法而不是其他方法。它们都是未定义的行为,但这使得你在做什么很明显,你是故意这样做的。