指向数据成员地址的指针

the*_*arc 5 c++ pointers

我读过(Inside 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);
}  
Run Code Online (Sandbox Code Playgroud)

应该打印偏移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;   
}
Run Code Online (Sandbox Code Playgroud)

为了区分p1和p2,每个实际成员偏移值都会增加1.因此,编译器(和用户)必须记住在实际使用该值来解析成员之前减去1.

pax*_*blo 11

某事物的偏移量是从一开始就有多少单位.第一件事是开始时它的偏移为零.

根据您在内存位置100的结构来考虑:

100: class X { int a;
104:           int b;
108:           int c;
Run Code Online (Sandbox Code Playgroud)

如您所见,地址a与整个结构的地址相同,因此其偏移量(您必须添加到结构地址以获取项目地址)为0.

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


而且,如果那本书你正在摘录的那本书真的Inside the C++ Object Model那么长,那就太长了.

事实上它来自'96并讨论了C++下的内部结构(对于知道它在哪里vptr是多么好的抒情,错过了那个在错误的抽象级别工作并且你永远不应该关心的重点)的日期相当多.事实上,引言甚至陈述"解释面向对象特征的基本实现 ......"(我的斜体).

事实上,没有人能够在ISO标准中找到任何说这种行为的事实,以及MSVC和gcc都没有采取这种行为的事实让我相信,即使过去的某个特定实现都是如此,所有这一切都不是真的(或必须是真的).

作者显然领导了cfront 2.1和3团队,虽然这本书似乎具有历史意义,但我认为它与现代C++语言(和实现)并不相关,至少我读过这些内容.


AnT*_*AnT 8

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

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

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

其次,我不明白你试图用你说话p1p2例子.假设指针包含相同的值是完全错误的.他们不会.

如果我们按照你的帖子中描述的方法("offset + 1"),那么p1将接收空指针的物理值(显然是物理的0x0),而p2whill接收物理值0x1(假设x有偏移0).0x0并且0x1是两个不同的价值观.

如果我们遵循现代GCC和MSVC++编译器使用的方法,那么p1将接收物理值0xFFFF....(空指针),同时p2将分配物理0x0.0xFFFF...并且0x0又是不同的价值观.

PS我刚刚意识到,p1p2例子其实不是你的,而是从一本书的报价.好了,这本书再一次描述了我上面提到的同样的问题 - 0偏移与0x0空指针表示的冲突,并提供了一种可行的方法来解决这种冲突.但是,再一次,有其他方法可以做到这一点,今天许多编译器使用完全不同的方法.

  • @MSN:这通常适用于指向成员*函数*的指针.指向*data*成员的指针明显更简单.它们对继承模型的敏感性明显较低(或者根本不敏感).通常,可以在*any*继承模型中将它们实现为普通偏移量.如果MSVC++正在做一些更复杂的事情,我不知道原因. (2认同)