reinterpret_cast几乎没用吗?

时间:2011-02-20 14:32:19

标签: c++ pointers casting reinterpret-cast type-punning

我之前已经阅读了有关the use of reinterpret_cast的各种问题,并且我还阅读了C ++标准中的相关措辞。本质上,它归结为指针指向reinterpret_cast操作的结果不能安全地用于任何,而不是被强制转换回原始指针类型。

然而,在实践中,reinterpret_cast的大多数实际用途似乎都基于({错误)假设reinterpret_cast与C风格的投射相同。例如,我看到很多代码使用reinterpret_castchar*转换为unsigned char*以用于字符集转换例程。这是完全无害的,但严格来说它不可移植 - 当您尝试取消引用{{1}时,无法保证从reinterpret_castchar*的{​​{1}}不会导致程序崩溃}指针。

根据标准,似乎其他实际使用的unsigned char*具有任何真正的保证,是从指针转换为整数,反之亦然。

然而,在很多情况下,我们想要(并且应该能够)在不同的指针类型之间安全地转换。例如:unsigned char*到新的C ++ 0x reinterpret_cast,或者实际上是指向与原始类型相同大小/对齐的基本数据类型的任何指针。然而,uint16_t*并不保证这应该起作用。

问题:我们如何安全地在指向相同大小/对齐的基本数据类型的指针之间进行转换,例如char16_t* - > reinterpret_cast?由于char*似乎并不能保证这实际上有效,因此C风格的转换是唯一安全的选择吗?

5 个答案:

答案 0 :(得分:11)

  

当您尝试取消引用unsigned char *指针时,无法保证从char *到unsigned char *的reinterpret_cast不会崩溃程序。

你不能以任何其他方式进行这样的演员,所以你必须要相信你的编译器对这个完全合理的演员所做的事情。

  

由于reinterpret_cast似乎并不能保证这实际上有效,C风格的转换是否是这里唯一安全的选择?

C风格的演员表只会映射到reinterpret_cast,所以它将完全相同。在某些时候,你必须信任你的编译器。标准有一个限制,它只是简单地说“不。阅读你的编译器手册”。当涉及到交叉投射指针时,这就是这一点。它允许您使用char左值阅读unsigned char。无法将char*转换为可用unsigned char*来执行此操作的编译器几乎无法使用,因此不存在。

答案 1 :(得分:4)

  

基本上,它归结为指针到指针reinterpret_cast操作的结果不能安全地用于除了被回送到原始指针类型之外的任何东西。

你是对的,标准被破坏了,但是N3242试图解决这个问题:

reinterpret_cast<T*>

的定义

N3242,[expr.reinterpret.cast]:

  

当“指向T1的指针”类型的prvalue v转换为“指向cv T2的指针”类型时,如果T1和T2都是标准布局类型(3.9)并且对齐,则结果为static_cast<cv T2*>(static_cast<cv void*>(v)) T2的要求不比T1严格。

这仍然没有定义;为了好玩,关于static_cast的不太相关的文字:

  

类型为“指向cv1 void的指针”的prvalue可以转换为类型为“指向cv2 T的指针”的prvalue,其中T是对象类型,cv2与cv-qualification相同,或者cv-qualification等于,cv1。空指针值将转换为目标类型的空指针值。指向对象的类型指针的值转换为“指向cv void的指针”并返回,可能具有不同的cv-qualification,应具有其原始值。

“适当转换”

N3242,[class.mem]:

  

指向标准布局结构对象的指针,适当使用reinterpret_cast转换,指向其初始成员(或者如果该成员是位字段,则指向该单元它居住在其中,反之亦然。

那是什么样的风格? “的适当地”?洛尔

显然,这些人不知道如何编写规范。

  

C风格在这里投出了唯一安全的选择吗?

没有帮助。

标准被打破,破碎,破碎。

但每个人都明白它的真正含义。标准的潜台词说:

  

当“指向T1的指针”类型的prvalue v转换为“指向cv T2的指针”类型时,结果static_cast<cv T2*>(static_cast<cv void*>(v)) 指向内存地址为v T1和T2是标准布局类型(3.9),T2的对齐要求不比T1更严格。

这当然不是完美的(但并不比标准的许多其他部分差),但是在2分钟内,我写了比委员会十年更好的规范。

答案 2 :(得分:3)

标准中的其他地方有一些保证(参见类型表示部分,IIRC,要求相应的未签名和签名类型共享公共值的表示,仍然是IIRC,还有一些文本保证您可以阅读任何东西作为字符)。但请注意,有些地方甚至会减少您正在阅读的部分(表明事情是实现定义和未指定的):某些形式的类型惩罚是未定义的行为。

答案 3 :(得分:2)

该标准规定了所有平台上必须发生的事情,您不必这样做。如果您将可移植性要求限制在reinterpret_cast实际工作的平台上,那很好。

在实际支持uint16_t的平台上,强制转换可能会起作用。在char16_t为18,24,32或36位宽的平台上,它可能做不到正确的事情。问题是,你是否必须支持这样的平台?语言标准想要。

答案 4 :(得分:0)

  

指针指向reinterpret_cast操作的结果不能安全地用于除了被转换回原始指针类型之外的任何其他操作。

这听起来不对。假设sizeof(void *) > sizeof(int *)void *foo; reinterpret_cast<void *>(reinterpret_cast<int *>(foo))可能会为您留下截断的指针值。

这不是reinterpret_cast - 在实践中 - 只是等同于C的默认演员吗?