如何评估指针和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
都返回true
,但这并不意味着上述内容必须true
。 &c
将通过隐式指针转换转换为适当的指针类型(A*
或B*
)。此转换将指针的值更改为 &c
指向的对象的各个基类子对象的地址。
从 cpp 首选项:
指向(可选 cv 限定)派生类类型的 prvalue 指针可以转换为指向其可访问、明确(与 cv 限定相同)基类的 prvalue 指针。 转换的结果是指向指向对象中的基类子对象的指针。空指针值将转换为目标类型的空指针值。
(强调我的)
A
是C
的第一个非虚基类,所以它直接放在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 |
|---------|
|---------|
当您将pa
和pb
与&c
进行比较时,会发生同样的事情 - 在第一种情况下,&c
是A
子对象的位置,在第二种情况下,它是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
,它们是不同的,因为A
和B
都是C
的基类,并且pa
指向c
的基类子对象A
,pb
指向c
的基类子对象B
, 实际内存地址将有所不同。它们不能/不应该指向相同的内存地址。
- 如何理解C++标准N3337中的expr.const.cast子句8
- C++Cast运算符过载
- 错误:"cast"未命名类型void setCastDescription(std::string
- 通过使用 const-cast 的非常量引用来延长临时的寿命
- "(void) cast"与功能有什么区别 "__attributes__"来沉默未使用的参数警告?
- C++:"Expected '(' for function-style cast or type construction"错误
- 为什么选择 g++ 给予者:"error: cast to pointer from integer of different size [-Werror=int-to-pointer-cast]"
- Gtk+ g_signal_connect() 和 C++ lambda 会导致"invalid cast"错误
- Shared_ptr cast vs static_cast speed
- C++20 中的严格别名规则是否允许标准 c++ unicode 字符和下划线类型之间"reinterpret
- 在 iOS 上使用 Aruco 构建 OpenCV 时"Functional-style cast from id to double is not allowed"
- 覆盖 CAST 运算符(我认为它被称为向下转换)
- C++错误,隐 <function-style-cast> 式要求使用模板化类一次调用多个构造函数的多个转换
- 如何修复<function-style-cast>错误:无法从'initializer list'转换为asdending比较<W>(模板函子)
- C++ C++ 中的函数声明,键入 CAST
- static_cast会丢弃错误,但C风格的演员cast有效
- C++ cast char * to unsigned char
- 禁用 Clang 中的"cast from pointer to smaller type uint32_t"错误
- 是否可以使用gcc 3.3版修复与int*cast相关的Sun Solaris OS 5.8分段故障
- C++ const-cast 一个引用