强制转换指针类型的正确方法

问题描述 投票:0回答:3

考虑以下代码(以及

VirtualAlloc()
返回
void*
的事实):

BYTE* pbNext = reinterpret_cast<BYTE*>(
    VirtualAlloc(NULL, cbAlloc, MEM_COMMIT, PAGE_READWRITE));

为什么选择

reinterpret_cast
而不是
static_cast

我曾经认为

reinterpret_cast
对于例如将指针转换为整数类型(例如
DWORD_PTR
),但从
void*
转换为
BYTE*
static_cast
不好吗?

在这种特殊情况下是否有任何(微妙的?)差异,或者它们只是有效的指针转换?

C++ 标准是否对这种情况有偏好,建议一种方法而不是另一种方法?

c++ pointers casting reinterpret-cast static-cast
3个回答
62
投票

对于基本类型的可转换指针,两种强制转换具有相同的含义;所以你是对的,

static_cast
没问题。

在某些指针类型之间进行转换时,指针中保存的特定内存地址可能需要更改

这就是两个演员阵容的不同之处。

static_cast
会做出适当的调整。
reinterpret_cast
不会。

因此,指针类型之间的

static_cast
是一个很好的一般规则,除非您知道需要
reinterpret_cast


34
投票

你应该

static_cast
如果要撤消隐式转换,请使用
static_cast

但是,在这种特殊情况下,没有区别,因为您是从

void*
转换的。但一般来说,两个对象指针之间的
reinterpret_cast
定义为 (§5.2.10/7):

对象指针可以显式转换为不同类型的对象指针。当“指向

v
”类型的纯右值
T1
转换为“指向 cv
T2
”的指针时,如果 static_cast<cv T2*>(static_cast<cv void*>(v))
T1
都是标准布局,则
结果为 
T2
 
T2
的类型和对齐要求并不比
T1
更严格,或者如果任一类型是
void
。将“指向
T1
的指针”类型的纯右值转换为“指向
T2
的指针”类型(其中
T1
T2
是对象类型,并且
T2
的对齐要求并不比
更严格) T1
)并返回其原始类型,产生原始指针值。任何其他此类指针转换的结果均未指定。

强调我的。由于

T1
对您来说已经是
void*
,因此在
void*
中转换为
reinterpret_cast
不会执行任何操作。一般来说,这不是真的,这就是 Drew Dormann 所说的

#include <iostream>

template <typename T>
void print_pointer(const volatile T* ptr)
{
    // this is needed by oversight in the standard
    std::cout << static_cast<void*>(const_cast<T*>(ptr)) << std::endl;
}

struct base_a {};
struct base_b {};
struct derived : base_a, base_b {};

int main()
{
    derived d;

    base_b* b = &d; // implicit cast

    // undo implicit cast with static_cast
    derived* x = static_cast<derived*>(b);

    // reinterpret the value with reinterpret_cast
    derived* y = reinterpret_cast<derived*>(b);

    print_pointer(&d);
    print_pointer(x);
    print_pointer(y);
}

输出:

00CBFD5B
00CBFD5B
00CBFD5C

(请注意,因为

y
实际上并不指向
derived
,所以使用它是未定义的行为。)

在这里,

reinterpret_cast
会产生不同的值,因为它经过了
void*
。这就是为什么您应该在可以时使用
static_cast
,在必须时使用
reinterpret_cast


8
投票

使用

static_cast
void*
之间转换指针可以保证保留地址。

另一方面,

reinterpret_cast
保证,如果将指针从一种类型转换为另一种类型,然后又转换回原始类型,则地址将被保留。

尽管在大多数实现中,使用其中任何一个都会看到相同的结果,但

static_cast
应该是首选。

对于

C++11
我记得,使用
reinterpret_cast
代表
void*
具有明确定义的行为。在此之前,这种行为是被禁止的。

不允许使用reinterpret_cast在指向对象类型的指针和指向void的指针之间进行转换。 ...

拟议决议(2010 年 8 月):

将 5.2.10 [expr.reinterpret.cast] 第 7 段更改如下:

对象指针可以显式转换为以下对象指针 不同的类型。当“指向 T1 的指针”类型的纯右值 v 为 转换为“指向 cv T2 的指针”类型,如果 T1 和 T2 都是标准布局,则结果为

static_cast<cv T2*>(static_cast<cv void*>(v))
types (3.9 [basic.types]) 和 T2 的对齐要求没有 比 T1 更严格,或者任一类型无效。

将“指向 T1 的指针”类型的纯右值转换为“指向 T1 的指针”类型 T2”(其中 T1 和 T2 是对象类型,对齐方式 T2 的要求并不比 T1 严格) 并回到它的 原始类型产生原始指针值。任何结果 其他此类指针转换未指定。

更多信息请参阅 C++ 标准核心语言缺陷报告和已接受的问题,修订版 115,问题 #1120、

reinterpret_cast
void*

感谢Jesse Good提供链接。

© www.soinside.com 2019 - 2024. All rights reserved.