如何评估指针和reinterpret_cast?
How does evaluate pointers and reinterpret_cast?
我在 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;
}
这是如何工作的?
pa
和pb
其实是不一样的。一种测试方法是:
reinterpret_cast<char*>(pa) == reinterpret_cast<char*>(pb)
pa == &c
和pb == &c
都是returntrue
,但这并不意味着上面一定是true
。 &c
将通过隐式指针转换转换为适当的指针类型(A*
或 B*
)。此转换将指针的值更改为 &c
.
指向的对象的相应基 class 子对象的地址
来自 cppreference:
A prvalue pointer to a (optionally cv-qualified) derived class type can be converted to a prvalue pointer to its accessible, unambiguous (identically cv-qualified) base class. The result of the conversion is a pointer to the base class subobject within the pointed-to object. The null pointer value is converted to the null pointer value of the destination type.
(强调我的)
A
是C
的第一个非虚基class,所以直接放在C
的内存开头space,即:
reinterpret_cast<char*>(pa) == reinterpret_cast<char*>(&c)
是true
。但是,B
子对象排在A
之后,不可能满足上述条件。隐式转换和 static_cast
都会为您提供基本子对象的正确地址。
如果你添加一些额外的输出,你可以看到发生了什么;我添加了以下行:
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,并将 return 为您提供 B 变量的值。令人困惑的是你的第二个三元运算符显示为真。这是(我假设)因为 B 的地址在 C 地址的范围内。
你比较的是地址 pa
和 pb
直接指向,它们是不同的,因为 A
和 B
都是基于 class C
的 pa
指向 c
的基 class 子对象 A
,pb
指向基 class subobject B
of c
,实际的内存地址会不同。它们不能t/shouldn指向相同的内存地址。
一个 C
实例有一个 A
子对象和一个 B
子对象。
像这样:
|---------|
|---------|
| A |
|---------|
C: |---------|
| B |
|---------|
|---------|
现在,
A *pa = &c;
使pa
指向A
子对象的位置,并且
B *pb = &c;
使 pb
指向 B
子对象的位置。
|---------|
|---------| <------ pa
| A |
|---------|
C: |---------| <------ pb
| B |
|---------|
|---------|
当您将 pa
和 pb
与 &c
进行比较时,会发生同样的事情 - 在第一种情况下,&c
是 [=15= 的位置] 子对象,第二个是 B
子对象的位置。
所以它们比较等于 &c
的原因是表达式 &c
在比较中实际上有不同的值(和不同的类型)。
当您 reinterpret_cast
时,不会进行任何调整 - 这意味着 "take the representation of this value and interpret it as representing a value of a different type"。
由于子对象位于不同的位置,因此将它们重新解释为 char
的位置的结果也不同。
我在 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;
}
这是如何工作的?
pa
和pb
其实是不一样的。一种测试方法是:
reinterpret_cast<char*>(pa) == reinterpret_cast<char*>(pb)
pa == &c
和pb == &c
都是returntrue
,但这并不意味着上面一定是true
。 &c
将通过隐式指针转换转换为适当的指针类型(A*
或 B*
)。此转换将指针的值更改为 &c
.
来自 cppreference:
A prvalue pointer to a (optionally cv-qualified) derived class type can be converted to a prvalue pointer to its accessible, unambiguous (identically cv-qualified) base class. The result of the conversion is a pointer to the base class subobject within the pointed-to object. The null pointer value is converted to the null pointer value of the destination type.
(强调我的)
A
是C
的第一个非虚基class,所以直接放在C
的内存开头space,即:
reinterpret_cast<char*>(pa) == reinterpret_cast<char*>(&c)
是true
。但是,B
子对象排在A
之后,不可能满足上述条件。隐式转换和 static_cast
都会为您提供基本子对象的正确地址。
如果你添加一些额外的输出,你可以看到发生了什么;我添加了以下行:
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,并将 return 为您提供 B 变量的值。令人困惑的是你的第二个三元运算符显示为真。这是(我假设)因为 B 的地址在 C 地址的范围内。
你比较的是地址 pa
和 pb
直接指向,它们是不同的,因为 A
和 B
都是基于 class C
的 pa
指向 c
的基 class 子对象 A
,pb
指向基 class subobject B
of c
,实际的内存地址会不同。它们不能t/shouldn指向相同的内存地址。
一个 C
实例有一个 A
子对象和一个 B
子对象。
像这样:
|---------|
|---------|
| A |
|---------|
C: |---------|
| B |
|---------|
|---------|
现在,
A *pa = &c;
使pa
指向A
子对象的位置,并且
B *pb = &c;
使 pb
指向 B
子对象的位置。
|---------|
|---------| <------ pa
| A |
|---------|
C: |---------| <------ pb
| B |
|---------|
|---------|
当您将 pa
和 pb
与 &c
进行比较时,会发生同样的事情 - 在第一种情况下,&c
是 [=15= 的位置] 子对象,第二个是 B
子对象的位置。
所以它们比较等于 &c
的原因是表达式 &c
在比较中实际上有不同的值(和不同的类型)。
当您 reinterpret_cast
时,不会进行任何调整 - 这意味着 "take the representation of this value and interpret it as representing a value of a different type"。
由于子对象位于不同的位置,因此将它们重新解释为 char
的位置的结果也不同。