指向数据成员地址的指针 我已经读到(C++对象模型内部),C++中数据成员指针的地址是数据成员加1的偏移量 我正在VC++2005上尝试此操作,但没有得到精确的偏移量值。 例如: Class X{ public: int a; int b; int c; } void x(){ printf("Offsets of a=%d, b=%d, c=%d",&X::a,&X::b,&X::c); }

指向数据成员地址的指针 我已经读到(C++对象模型内部),C++中数据成员指针的地址是数据成员加1的偏移量 我正在VC++2005上尝试此操作,但没有得到精确的偏移量值。 例如: Class X{ public: int a; int b; int c; } void x(){ printf("Offsets of a=%d, b=%d, c=%d",&X::a,&X::b,&X::c); },c++,pointers,C++,Pointers,应打印a=1、b=5、c=9的偏移量。但在VC++2005中,结果是a=0,b=4,c=8。 我无法理解这种行为。 摘自此书: 然而,这一预期与传统错误相差一个百分点 对于C和C++程序员来说, 类中三个坐标成员的物理偏移 如果vptr位于,则布局分别为0、4和8 如果将vptr放置在机器的开始位置,则为4、8和12的末端 类。但是,从获取成员地址返回的值, 总是加1。因此,实际值为1、5和9,以及 以此类推,问题在于区分不指向数据的指针 成员和指向第一个数据成员的指针。例如,考虑: float

应打印a=1、b=5、c=9的偏移量。但在VC++2005中,结果是a=0,b=4,c=8。
我无法理解这种行为。
摘自此书:

然而,这一预期与传统错误相差一个百分点 对于C和C++程序员来说,

类中三个坐标成员的物理偏移 如果vptr位于,则布局分别为0、4和8 如果将vptr放置在机器的开始位置,则为4、8和12的末端 类。但是,从获取成员地址返回的值, 总是加1。因此,实际值为1、5和9,以及 以此类推,问题在于区分不指向数据的指针 成员和指向第一个数据成员的指针。例如,考虑:

float Point3d::*p1 = 0;   
float Point3d::*p2 = &Point3d::x;   

// oops: how to distinguish?   
if ( p1 == p2 ) {   
   cout << " p1 & p2 contain the same value — ";   
   cout << " they must address the same member!" << endl;   
}
float Point3d::*p1=0;
浮点3D::*p2=&Point3d::x;
//哎呀,怎么区分呢?
如果(p1==p2){

cout某事物的偏移量是它距起点的单位数。第一件事是在起点,所以它的偏移量为零

考虑到您的结构位于内存位置100:

100: class X { int a;
104:           int b;
108:           int c;
如您所见,
a
的地址与整个结构的地址相同,因此其偏移量(必须添加到结构地址才能获得项目地址)为0

请注意,ISO标准没有指定项目在内存中的布局位置。填充字节以创建正确的对齐方式当然是可能的。在假设的环境中,INT只有两个字节,但它们所需的对齐方式是256个字节,它们不是0、2和4,而是0、256和512


和,如果你正在摘录的这本书是真正的 > C++对象模型,它的牙齿会变长。

从96中讨论C++内部的事实(关于如何知道“<代码> VPTR < /代码>是多么好,错过了在错误的抽象级别上工作的全部点,你永远不应该关心的事情”)。解释面向对象功能的基本实现…(我的斜体)

事实上,没有人能在ISO标准中找到任何说明这种行为是必需的,而且MSVC和gcc都不是这样做的,这让我相信,即使在过去的某个特定实现中是这样的,也不是所有的实现都是这样的(或必须是这样的)


<>作者显然领导了CVAX 2.1和3个团队,虽然这本书似乎有历史意义,但我认为它与现代C++语言(和实现)无关,至少我读过的那些词。

< P>你的行为看起来很合理。你读到的东西听起来有问题。

我已经读到了指针的地址 C++中的数据成员是 数据成员加1

<>我从未听说过,你自己的经验证据表明情况并非如此。我认为你误解了C++中结构和类的一个奇特性质。如果它们完全是空的,它们的大小是1(这样它们的数组中的每个元素都有一个唯一的地址)

$92/12是有趣的< /p> 在没有插入访问说明符的情况下声明的(非联合)类的非静态数据成员被分配,以便以后的成员在类对象中具有更高的地址。未指定由访问说明符分隔的非静态数据成员的分配顺序(11.1)。实现一致性要求可能会导致两个相邻的成员不能紧跟在一起分配;管理虚拟函数(10.3)和虚拟基类(10.1)的空间要求也可能如此


这解释了这种行为是由实现定义的。然而,“a”、“b”和“c”位于递增地址的事实符合标准。

首先,指向数据成员类型的指针的值的内部表示是一个实现细节。它可以通过许多不同的方式完成。您遇到了一个描述在一个可能的实现中,指针包含成员加1的偏移量。很明显,“加1”来自何处:特定的实现希望为空指针保留物理零值(
0x0
),因此第一个数据成员的偏移量(很容易为0)必须转换为其他类型才能使其不同于空指针。向所有此类指针添加1可以解决此问题

但是,应该注意的是,这是一种相当麻烦的方法(即,编译器在执行访问之前总是必须从物理值中减去1)。该实现显然非常努力确保所有空指针都由物理零位模式表示。说实话,我最近在实践中没有遇到过采用这种方法的实现

今天,大多数流行的实现(如GCC或MSVC++)只使用普通偏移量(不向其添加任何内容)作为指向数据成员的指针的内部表示。当然,物理零将不再用于表示空指针,因此它们使用其他一些物理值来表示空指针,如
0xFFFF…
(这是GCC和MSVC++使用的)

其次,我不明白你想用你的
p1
p2
示例说什么。你认为指针将包含相同的值是绝对错误的。它们不会

如果我们遵循您文章中描述的方法(“偏移量+1”),那么
p1
将收到null指针的物理值(显然是一个物理值)<
void test()
{  
    using namespace std;

    int X::* pm = NULL;
    cout << "NULL pointer to member: "
        << " value = " << pm 
        << ", raw byte value = 0x" << hex << *(unsigned int*)&pm << endl;

    pm = &X::a;
    cout << "pointer to member a: "
        << " value = " << pm 
        << ", raw byte value = 0x" << hex << *(unsigned int*)&pm << endl;

    pm = &X::b;
    cout << "pointer to member b: "
        << " value = " << pm 
        << ", raw byte value = 0x" << hex << *(unsigned int*)&pm << endl;
}
NULL pointer to member:  value = 0, raw byte value = 0xffffffff
pointer to member a:  value = 1, raw byte value = 0x0
pointer to member b:  value = 1, raw byte value = 0x4