一个简短的例子输出一个奇怪的结果!
#include <iostream>
using namespace std;
struct A { int a; };
struct B { int b; };
struct C : A, B
{
int c;
};
int main()
{
C* c = new C;
B* b = c;
cout << "The address of b is 0x" << hex << b << endl;
cout << "The address of c is 0x" << hex << c << endl;
if (b == c)
{
cout << "b is equal to c" << endl;
}
else
{
cout << "b is not equal to c" << endl;
}
}
令我非常惊讶的是,输出应该如下:
The address of b is 0x003E9A9C
The address of c is 0x003E9A98
b is equal to c
令我惊讶的是:
0x003E9A9C不等于0x003E9A98,但输出为“b等于c”
答案 0 :(得分:87)
C
对象包含两个子对象,类型为A
和B
。显然,这些必须具有不同的地址,因为两个单独的对象不能具有相同的地址;所以最多其中一个可以与C
对象具有相同的地址。这就是为什么打印指针会给出不同的值。
比较指针并不是简单地比较它们的数值。只能比较相同类型的指针,因此必须转换第一个指针以匹配另一个指针。在这种情况下,c
会转换为B*
。这与用于初始化b
的转换完全相同:它调整指针值,使其指向B
子对象而不是C
对象,并且两个指针现在比较相等。
答案 1 :(得分:74)
类型为C
的对象的内存布局如下所示:
| <---- C ----> |
|-A: a-|-B: b-|- c -|
0 4 8 12
我在对象的地址中添加了偏移量(在像你这样的平台中,sizeof(int)= 4)。
在您的主要内容中,您有两个指针,为了清楚起见,我会将它们重命名为pb
和pc
。 pc
指向整个C对象的开头,而pb
指向B子对象的开头:
| <---- C ----> |
|-A: a-|-B: b-|- c -|
0 4 8 12
pc-^ pb-^
这就是他们的价值观不同的原因。 3E9A98 + 4是3E9A9C,十六进制。
如果你现在比较这两个指针,编译器会看到B*
和C*
之间的比较,它们是不同的类型。所以它必须应用隐式转换(如果有的话)。 pb
无法转换为C*
,但反过来也可以 - pc
转换为B*
。该转换将指向指向pc
所指向的B子对象的指针 - 它与您定义B* pb = pc;
时使用的隐式转换相同。结果显然等于pb
:
| <---- C ----> |
|-A: a-|-B: b-|- c -|
0 4 8 12
pc-^ pb-^
(B*)pc-^
因此,在比较两个指针时,编译器实际上会比较转换的指针,它们是相等的。
答案 2 :(得分:8)
我知道有一个答案,但也许这会更直接,并通过一个例子备份。
此处C*
B*
操作数上隐式转换c
到if (b == c)
如果你使用这段代码:
#include <iostream>
using namespace std;
struct A { int a; };
struct B { int b; };
struct C : A, B
{
int c;
};
int main()
{
C* c = new C;
B* b = c;
cout << "The address of b is 0x" << hex << b << endl;
cout << "The address of c is 0x" << hex << c << endl;
cout << "The address of (B*)c is 0x" << hex << (B*)c << endl;
if (b == c)
{
cout << "b is equal to c" << endl;
}
else
{
cout << "b is not equal to c" << endl;
}
}
你得到:
The address of b is 0x0x88f900c
The address of c is 0x0x88f9008
The address of (B*)c is 0x0x88f900c
b is equal to c
c
投放到B*
类型的地址与b
的地址相同。正如所料。
答案 3 :(得分:7)
如果我可以添加Mike的优秀答案,如果你将它们转换为void*
,那么你将获得预期的行为:
if ((void*)(b) == (void*)(c))
^^^^^^^ ^^^^^^^
打印
b is not equal to c
在C(语言)上做类似的事情实际上激怒了编译器,因为比较了不同类型的指针。
我得到了:
warning: comparison of distinct pointer types lacks a cast [enabled by default]
答案 4 :(得分:2)
在计算中(或者说,我们应该在数学中说)可以有许多平等概念。任何对称,反身和传递的关系都可以用作平等。
在你的程序中,你正在研究两种不同的相等概念:按位实现标识(两个指针指向完全相同的地址)与另一种基于对象标识的相等性,它允许同一对象上的两个视图,通过不同静态类型的引用,被恰当地视为引用相同的对象。
这些不同类型的视图使用不具有相同地址值的指针,因为它们会锁定到对象的不同部分。编译器知道这一点,因此它为相等比较生成正确的代码,该代码比较考虑了这个偏移量。
这是由继承带来的对象的结构,这使得必须具有这些偏移。当存在多个基数时(由于多重继承),这些基数中只有一个可以位于对象的低地址,因此指向基本部分的指针与指向派生对象的指针相同。其他基础部分在对象的其他地方。
因此,根据面向对象的对象视图,指针的天真,按位比较不会产生正确的结果。
答案 5 :(得分:1)
这里有一些好的答案,但有一个简短的版本。 “两个对象是相同的”并不意味着它们具有相同的地址。这意味着将数据放入其中并从中获取数据是等效的。