如何评估指针和reinterpret_cast

How does evaluate pointers and reinterpret_cast?

本文关键字:reinterpret cast 指针 何评估 评估      更新时间:2023-10-16

我在Visual Studio中运行了以下代码。c 的地址与指向pa的地址相同,但与pb 的地址不同。然而,两个三元运算符都将计算为 true ,这是在调试器中仅查看代码而不看到 pa 和 pb 的指向地址所期望的。第三个三元运算符的计算结果为 false

#include <iostream>
class A
{
public:
    A() : m_i(0) {}
protected:
    int m_i;
};
class B
{
public:
    B() : m_d(0.0) {}
protected:
    double m_d;
};
class C
    : public A
    , public B
{
public:
    C() : m_c('a') {}
private:
    char m_c;
};
int main()
{
    C c;
    A *pa = &c;
    B *pb = &c;
    const int x = (pa == &c) ? 1 : 2;
    const int y = (pb == &c) ? 3 : 4;
    const int z = (reinterpret_cast<char*>(pa) == reinterpret_cast<char*>(pb)) ? 5 : 6;
    std::cout << x << y << z << std::endl;
    return 0;
}

这是如何工作的?

papb实际上是不同的。测试的一种方法是:

reinterpret_cast<char*>(pa) == reinterpret_cast<char*>(pb)

pa == &cpb == &c都返回true,但这并不意味着上述内容必须true&c将通过隐式指针转换转换为适当的指针类型(A*B*)。此转换将指针的值更改为 &c 指向的对象的各个基类子对象的地址。

从 cpp 首选项:

指向(可选 cv 限定)派生类类型的 prvalue 指针可以转换为指向其可访问、明确(与 cv 限定相同)基类的 prvalue 指针。 转换的结果是指向指向对象中的基类子对象的指针。空指针值将转换为目标类型的空指针值。

(强调我的)


AC的第一个非虚基类,所以它直接放在C内存空间的开头,即:

reinterpret_cast<char*>(pa) == reinterpret_cast<char*>(&c)

true.但是,B子对象是在A之后布置的,所以它不可能满足上述条件。然后,隐式转换和static_cast都为您提供基子对象的正确地址。

C实例具有A子对象和B子对象。
像这样:

    |---------|
    |---------|
    |    A    |
    |---------|
 C: |---------|
    |    B    |
    |---------|
    |---------|

现在

A *pa = &c;

使pa指向A子对象的位置,并且

B *pb = &c;

使pb指向B子对象的位置。

    |---------|
    |---------| <------ pa
    |    A    |  
    |---------|
 C: |---------| <------ pb
    |    B    |  
    |---------|
    |---------|

当您将papb&c进行比较时,会发生同样的事情 - 在第一种情况下,&cA子对象的位置,在第二种情况下,它是B子对象的位置。
因此,它们都等于&c的原因是表达式&c实际上在比较中具有不同的值(和不同的类型)。

当您reinterpret_cast 时,不会进行任何调整 - 这意味着"获取此值的表示并将其解释为表示不同类型的值"。
由于子对象位于不同的位置,因此将它们重新解释为char位置的结果也不同。

如果你添加一些额外的输出,你可以看到发生了什么; 我添加了以下行:

std::cout << "pa: " << pa << "; pb: " << pb << "; c: " << &c <<  std::endl;

当然,它的输出会有所不同,因为我正在打印指针的值,但它看起来像:

pa: 0x1000 pb: 0x1008 c: 0x1000

pb 指针实际上指向 pa + sizeof(int)(在我的 64 位机器上是 8 个字节)。 这是因为当您执行以下操作时:

B *pb = &c;

编译器将 C 对象强制转换为 B,并将返回 B 变量的值。 令人困惑的是,您的第二个三元运算符显示为真。 这是(我假设)因为 B 的地址在 C 地址的范围内。

您正在比较地址pa和直接指向pb,它们是不同的,因为AB都是C的基类,并且pa指向c的基类子对象Apb指向c的基类子对象B, 实际内存地址将有所不同。它们不能/不应该指向相同的内存地址。