Reinterpret_cast 与 C 风格的转换

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/7831696/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-28 17:33:34  来源:igfitidea点击:

Reinterpret_cast vs. C-style cast

c++casting

提问by user103214

I hear that reinterpret_castis implementation defined, but I don't know what this really means. Can you provide an example of how it can go wrong, and it goes wrong, is it better to use C-Style cast?

我听说这reinterpret_cast是实现定义的,但我不知道这到底意味着什么。你能提供一个例子来说明它是如何出错的,它出错了,使用 C-Style cast 会更好吗?

回答by jalf

The C-style cast isn't better.

C 风格的演员不是更好。

It simply tries the various C++-style casts in order, until it finds one that works. That means that when it acts like a reinterpret_cast, it has the exact same problems as a reinterpret_cast. But in addition, it has these problems:

它只是按顺序尝试各种 C++ 风格的类型转换,直到找到一个有效的类型。这意味着当它像 a 一样运行时reinterpret_cast,它有与 a 完全相同的问题reinterpret_cast。但除此之外,它还存在以下问题:

  • It can do many different things, and it's not always clear from reading the code which type of cast will be invoked (it might behave like a reinterpret_cast, a const_castor a static_cast, and those do very different things)
  • Consequently, changing the surrounding code might change the behaviour of the cast
  • It's hard to find when reading or searching the code - reinterpret_castis easy to find, which is good, because casts are ugly and should be paid attention to when used. Conversely, a C-style cast (as in (int)42.0) is much harder to find reliably by searching
  • 它可以做很多不同的事情,并且通过阅读代码并不总是清楚将调用哪种类型的强制转换(它可能表现得像 a reinterpret_cast、 aconst_cast或 a static_cast,并且它们做的事情非常不同)
  • 因此,改变周围的代码可能会改变演员表的行为
  • 阅读或搜索代码时很难找到 -reinterpret_cast很容易找到,这很好,因为强制转换很丑陋,使用时应注意。相反,(int)42.0通过搜索很难可靠地找到C 风格的类型转换(如)

To answer the other part of your question, yes, reinterpret_castis implementation-defined. This means that when you use it to convert from, say, an int*to a float*, then you have no guarantee that the resulting pointer will point to the same address. That part is implementation-defined. But if you take the resulting float*and reinterpret_castit back into an int*, then you will get the original pointer. That part is guaranteed.

要回答问题的另一部分,是的,reinterpret_cast是实现定义的。这意味着当您使用它从 an 转换int*为 a 时float*,您不能保证结果指针将指向相同的地址。那部分是实现定义的。但是,如果你把所得到的float*reinterpret_cast它放回的int*,那么你会得到原来的指针。那部分是有保证的。

But again, remember that this is true whether you use reinterpret_castor a C-style cast:

但同样,请记住,无论您使用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

回答by Konstantin Oznobihin

It is implementation defined in a sense that standard doesn't (almost) prescribe how different types values should look like on a bit level, how address space should be structured and so on. So it's really a very platform specific for conversions like:

它是在某种意义上定义的实现,标准没有(几乎)规定不同类型的值在位级别上应该如何,地址空间应该如何结构等等。因此,它确实是一个非常适合转换的平台,例如:

double d;
int &i = reinterpret_cast<int&>(d);

However as standard says

然而正如标准所说

It is intended to be unsurprising to those who know the addressing structure of the underlying machine.

对于了解底层机器寻址结构的人来说,这并不奇怪。

So if you know what you do and how it all looks like on a low-level nothing can go wrong.

因此,如果您知道自己在做什么以及在低级别上看起来如何,那么就不会出错。

The C-style cast is somewhat similar in a sense that it can perform reinterpret_cast, but it also "tries" static_cast first and it can cast away cv qualification (while static_cast and reinterpret_cast can't) and perform conversions disregarding access control (see 5.4/4 in C++11 standard). E.g.:

C 风格的强制转换在某种意义上有点相似,它可以执行 reinterpret_cast,但它也首先“尝试”static_cast 并且它可以抛弃 cv 限定(而 static_cast 和 reinterpret_cast 不能)并执行转换而忽略访问控制(见 5.4 /4 在 C++11 标准中)。例如:

#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:\t\t" << b2 << "\n";
  cout << "no cast:\t\t" << &c << "\n";
}

and here is an output from ideone:

这是 ideone 的输出:

reinterpret_cast:  0xbfd84e78
C-style cast:      0xbfd84e7c
no cast:           0xbfd84e78

note that value produced by reinterpret_cast is exactly the same as an address of 'c', while C-style cast resulted in a correctly offset pointer.

请注意,reinterpret_cast 产生的值与 'c' 的地址完全相同,而 C 风格的强制转换产生了正确偏移的指针。

回答by edA-qa mort-ora-y

There are valid reasons to use reinterpret_cast, and for these reasons the standard actually defines what happens.

有正当理由使用reinterpret_cast,并且由于这些原因,标准实际上定义了会发生什么。

The first is to use opaque pointer types, either for a library API or just to store a variety of pointers in a single array (obviously along with their type). You are allowed to convert a pointer to a suitably sized integer and then back to a pointer and it will be the exact same pointer. For example:

第一种是使用不透明指针类型,用于库 API 或只是将各种指针存储在单个数组中(显然连同它们的类型)。您可以将指针转换为适当大小的整数,然后再转换回指针,它将是完全相同的指针。例如:

T b;
intptr_t a = reinterpret_cast<intptr_t>( &b );
T * c = reinterpret_cast<T*>(a);

In this code cis guaranteed to point to the object bas you'd expected. Conversion back to a different pointer type is of course undefined (sort of).

在此代码c中保证b如您所愿指向对象。转换回不同的指针类型当然是未定义的(有点)。

Similar conversions are allowed for function pointers and member function pointers, but in the latter case you can cast to/from another member function pointer simply to have a variable that is big enouhg.

函数指针和成员函数指针允许类似的转换,但在后一种情况下,您可以简单地向/从另一个成员函数指针进行转换,以获得足够大的变量。

The second case is for using standard layout types. This is something that was de factor supported prior to C++11 and has now been specified in the standard. In this case the standard treats reinterpret_cast as a static_cast to void* first and then a static_cast to the desination type. This is used a lot when doing binary protocols where data structures often have the same header information and allows you to convert types which have the same layout, but differ in C++ class structure.

第二种情况是使用标准布局类型。这是在 C++11 之前被实际支持的东西,现在已经在标准中指定。在这种情况下,标准首先将 reinterpret_cast 视为对 void* 的 static_cast,然后再对目标类型进行 static_cast。这在执行二进制协议时经常使用,其中数据结构通常具有相同的标头信息,并允许您转换具有相同布局但在 C++ 类结构中不同的类型。

In both of these cases you should use the explicit reinterpret_castoperator rather than the C-Style. Though the C-style would normally do the same thing, it has the danger of being subjected to overloaded conversion operators.

在这两种情况下,您应该使用显式reinterpret_cast运算符而不是 C 样式。尽管 C 风格通常会做同样的事情,但它有遭受重载转换运算符的危险。

回答by Ayjay

C++ has types, and the only way they normally convert between each other is by well-defined conversion operators that you write. In general, that's all you both need and should use to write your programs.

C++ 具有类型,它们之间通常相互转换的唯一方法是通过您编写的明确定义的转换运算符。一般来说,这就是你们编写程序所需要和应该使用的全部内容。

Sometimes, however, you want to reinterpret the bits that represent a type into something else. This is usually used for very low-level operations and is not something you should typically use. For those cases, you can use reinterpret_cast.

但是,有时您希望将表示类型的位重新解释为其他内容。这通常用于非常低级的操作,通常不应该使用它。对于这些情况,您可以使用reinterpret_cast.

It is implementation defined because the C++ standard does not really say much at all about how things should actually be laid out in memory. That is controlled by your specific implementation of C++. Because of this, the behaviour of reinterpret_castdepends upon how your compiler lays structures out in memory and how it implements reinterpret_cast.

它是实现定义的,因为 C++ 标准并没有真正说明应该如何在内存中实际布局。这由您对 C++ 的特定实现控制。因此, 的行为reinterpret_cast取决于您的编译器如何在内存中布置结构以及它如何实现reinterpret_cast

C-style casts are quite similar to reinterpret_casts, but they have much less syntax and are not recommended. The thinking goes that casting is inherently an ugly operation and it requires ugly syntax to inform the programmer that something dubious is happening.

C 风格的强制转换与reinterpret_casts非常相似,但它们的语法要少得多,不推荐使用。这种想法认为强制转换本质上是一个丑陋的操作,它需要丑陋的语法来通知程序员一些可疑的事情正在发生。

An easy example of how it could go wrong:

一个简单的例子说明它如何出错:

std::string a;
double* b;
b = reinterpret_cast<double*>(&a);
*b = 3.4;

That program's behaviour is undefined - a compiler could do anything it likes to that. Most probably, you would get a crash when the string's destructor is called, but who knows! It might just corrupt your stack and cause a crash in an unrelated function.

那个程序的行为是未定义的——编译器可以做任何它喜欢的事情。最有可能的是,当string调用 's 的析构函数时,您会崩溃,但谁知道呢!它可能只会破坏您的堆栈并导致无关函数崩溃。

回答by B?ови?

Both reinterpret_castand c-style casts are implementation defined and they do almost the same thing. The differences are :
1. reinterpret_castcan not remove constness. For example :

无论reinterpret_cast和C风格的转换是实现定义的,他们这样做几乎同样的事情。不同之处在于:
1.reinterpret_cast不能去除constness。例如 :

const unsigned int d = 5;
int *g=reinterpret_cast< int* >( &d );

will issue an error :

会报错:

error: reinterpret_cast from type 'const unsigned int*' to type 'int*' casts away qualifiers  

2. If you use reinterpret_cast, it is easy to find the places where you did it. It is not possible to do with c-style casts

2. 如果你使用reinterpret_cast,很容易找到你做过的地方。不可能使用 c 风格的强制转换

回答by Davislor

C-style casts sometimes type-pun an object in an unspecified way, such as (unsigned int)-1, sometimes convert the same value to a different format, such as (double)42, sometimes could do either, like how (void*)0xDEADBEEFreinterprets bits but (void*)0is guaranteed to be a null pointer constant, which does not necessarily have the same object representation as (intptr_t)0, and very rarely tells the compiler to do something like shoot_self_in_foot_with((char*)&const_object);.

C 风格的强制转换有时以未指定的方式对对象进行类型(unsigned int)-1转换,例如,有时将相同的值转换为不同的格式,例如(double)42,有时也可以这样做,例如如何(void*)0xDEADBEEF重新解释位但(void*)0保证是空指针常量,它不一定与 具有相同的对象表示(intptr_t)0,并且很少告诉编译器执行类似shoot_self_in_foot_with((char*)&const_object);.

That's usually all well and good, but when you want to cast a doubleto a uint64_t, sometimes you want the value and sometimes you want the bits. If you know C, you know which one the C-style cast does, but it's nicer in some ways to have different syntax for both.

这通常很好,但是当您想将 a 转换double为 a 时uint64_t,有时您需要值,有时您需要位。如果您了解 C,您就会知道 C 风格的类型转换是做什么的,但在某些方面对两者使用不同的语法会更好。

Bjarne Stroustrup, in his guidelines, recommended reinterpret_castin another context: if you want to type-pun in a way that the language does not define by a static_cast, he suggested that you do it with something like reinterpret_cast<double&>(uint64)rather than the other methods. They're all undefined behavior, but that makes it very explicit what you're doing and that you're doing it on purpose. Reading a different member of a union than you last wrote to does not.

Bjarne Stroustrup 在他的指导方针中建议reinterpret_cast在另一种情况下使用:如果您想以语言未定义的方式输入双关语static_cast,他建议您使用类似reinterpret_cast<double&>(uint64)的方法而不是其他方法。它们都是未定义的行为,但这使得您在做什么以及您是故意这样做的非常明确。读取与您上次写信不同的工会成员不会。