20

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

然而,在实践中,大多数现实世界的使用reinterpret_cast似乎都基于(错误的)假设,即 areinterpret_cast与 C 风格的演员表相同。例如,我看到很多代码用于转换reinterpret_castfrom char*tounsigned char*以实现字符集转换例程。这是完全无害的,但严格来说它不是可移植的 - 当您尝试取消引用指针时,不能保证reinterpret_castfrom char*tounsigned char*不会使您的程序崩溃。unsigned char*

根据标准,似乎唯一具有任何真正保证的其他真正用途reinterpret_cast是从指针转换为整数,反之亦然。

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

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

4

5 回答 5

12

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

您不能以任何其他方式进行此类转换,因此您必须相信您的编译器对这种完全合理的转换所做的事情。

由于 reinterpret_cast 似乎不能保证这确实有效,所以 C 风格的强制转换是这里唯一安全的选择吗?

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

于 2011-02-21T09:09:32.020 回答
4

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

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

值的定义reinterpret_cast<T*>

N3242,[expr.reinterpret.cast]:

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

这仍然没有定义任何东西;为了好玩,有关以下内容的无关紧要的文字static_cast

“指向 cv1 void 的指针”类型的纯右值可以转换为“指向 cv2 T 的指针”类型的纯右值,其中 T 是对象类型,而 cv2 与 cv1 具有相同的 cv 限定或大于 cv1 的 cv 限定。空指针值转换为目标类型的空指针值。指向对象的指针类型的值转换为“指向 cv void 的指针”并返回,可能具有不同的 cv 限定,应具有其原始值。

“适当转换”

N3242,[class.mem]:

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

那是一种什么样的风格?“合适”?哈哈

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

C 风格在这里强制转换是唯一安全的选择?

它没有帮助。

标准坏了,坏了,坏了。

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

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

这当然不是完美的(但并不比标准的许多其他部分差),但我用 200 万写了一个比委员会十年来写的更好的规范。

于 2011-12-11T17:26:34.807 回答
3

在标准的其他地方有一些保证(参见关于类型表示的部分,IIRC 要求相应的无符号和有符号类型共享公共值的表示,仍然是 IIRC,还有一些文本保证您可以将任何内容作为字符读取)。但还要注意,有些地方甚至会减少您正在阅读的部分(其中指出事物是实现定义和未指定的):某些形式的类型双关语是未定义的行为。

于 2011-02-20T14:57:12.827 回答
2

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

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

于 2011-02-20T14:42:37.630 回答
0

指针到指针 reinterpret_cast 操作的结果除了被强制转换回原始指针类型外,不能安全地用于任何事情。

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

这难道不是reinterpret_cast——在实践中——仅仅等同于 C 的默认转换吗?

于 2011-02-20T14:41:31.257 回答