可以从指向子对象的指针中获得指向完整对象表示元素的指针吗?

Posted

技术标签:

【中文标题】可以从指向子对象的指针中获得指向完整对象表示元素的指针吗?【英文标题】:Can one get a pointer to a complete object representation element from a pointer to a suboject? 【发布时间】:2017-10-13 21:49:52 【问题描述】:

让我们考虑一下这段代码:

int i;
int is[10];

unsigned char * p = reinterpret_cast<unsigned char*>(&i);
//p defined to point to the object-representation of the first element of array ints
unsigned char * ps = reinterpret_cast<unsigned char*>(&is[0]);

p += sizeof(int);
ps += sizeof(int);
//now ps points to the end of ints[0] and p point to the end of i;

p += sizeof(int); //Undefined behavior according to [expr.add]

ps += sizeof(int); //Undefined behavior?
unsigned char c = *ps;//Undefined behavior?

如果我们认为ps 指向is[0] 的object-representation,那么根据指针运算规则,最后两行代码的行为是未定义的。

不过,如果 ps 也是一个指向 int is 数组的object-representation 的指针,则定义了行为。

所以我的问题是:指向suboject 的object-representation 的指针是否也是指向包含的object-representation 元素的指针完整的对象?

【问题讨论】:

我认为您将“子对象”与“数组成员”混淆了。对于成员子对象,允许在同一个大对象内的指针之间进行比较,但不允许进行算术运算。对于指向数组元素的指针,可以使用指针算法。 @BenVoigt 如果您点击链接“suboject”,您将获得:“子对象可以是成员子对象([class.mem]),基类子对象([class.derived]),或数组元素”。 您是在问是否对字符类型使用offsetof 和指针算法进行了明确定义? @BenVoigt 我确实知道指针算术的规则。这里的问题是关于对象表示的指针算法 @RichardCritten 我对此表示怀疑,但显然,对象表示的概念与存储的概念紧密耦合。这是我问的问题,原因是我对 cme​​ts 有疑问,我的问题实际上是指针值是否有效?显然是的。 ***.com/questions/46735324/… 【参考方案1】:

一般情况

是的,指向子对象的对象表示的指针也是指向包含完整对象的部分对象表示的指针。

这可以推导出来:

1.8/1:对象是一个存储区域。

3.9/4: T 类型对象的对象表示是由 T 类型对象占用的 N 个 unsigned char 对象的序列,其中 N 等于 sizeof(T)。

5.3.3/1: sizeof 运算符产生其操作数的对象表示中的字节数。

1.8/2:对象可以包含其他对象,称为子对象。子对象可以是成员子对象、基类子对象、 或数组元素。不是子对象的对象 任何其他对象都称为完整对象。

对象是存储区域并且子对象包含在存储区域内的事实意味着子对象表示包含在对象表示中。

正如this other answer 中所解释的,指向对象表示的指针实际上是指向对象的指针。

数组的具体情况

是的,指向子对象的对象表示(即在本例中为数组元素)的指针是指向一个元素的指针包含完整对象的对象表示(即在此情况下的数组)。

8.4.3/1: (...) 数组类型的对象包含一个连续分配的非空集合,由 N 个 T 类型的子对象组成。 (. ..)

所以根据定义:

数组的元素是连续分配的,这意味着元素之间没有空白空间。

每个元素的对象表示是 sizeof(T) 个无符号字符的序列,完整数组的对象表示是生成的 N*sizeof(T) 个无符号字符。

我们可以推断:

正如this other answer 中所解释的,指向对象表示的指针实际上就是指向对象的指针。

数组中第 k 个元素的地址对应于数组对象表示中的第 k 个元素,并且是在 k*sizeof(T) 无符号字符的偏移量处构造的数组表示的地址。

所以你的代码的最后两条语句不是未定义的行为,只要你保持在is的边界内

【讨论】:

【参考方案2】:

没有“指向对象表示的指针”这样的东西。您有一个保存对象地址的指针,该指针可以安全或不安全地派生。

当您使用窄字符类型的左值在对象的地址范围内读取时,“对象表示”开始发挥作用,而不是在形成或转换指针时。

reinterpret_cast&lt;unsigned char*&gt;(p) 的规则中没有任何内容说明结果是一些特殊的“指向对象表示的指针”。它仍然只是同一对象的另一个别名。

【讨论】:

谢谢。所以你的意思是auto ps = reinterpret_cast&lt;unsigned char*&gt; (&amp;is[0])是数组is的第一个元素的别名,所以ps+2*sizeof(int)是未定义的行为,即使ps+2*sizeof(int)指向的内存在is的地址范围内? 只是一个评论,我认为 [basic.stc.dynamic.safety] 在这里不适用,因为我们没有处理由new 分配的指针,无论如何,所有实现都有 宽松的指针安全性. @Oliv:不,我没有说这是未定义的行为。它是数组成员的地址,指针算术规则允许添加位于同一数组内的任何偏移量。这使您的示例得以定义。

以上是关于可以从指向子对象的指针中获得指向完整对象表示元素的指针吗?的主要内容,如果未能解决你的问题,请参考以下文章

指向不完整类型的指针可以不完整吗?

删除时出错:表达式必须是指向完整对象类型的指针

C ++在向量迭代中删除并返回指向对象的指针

在删除指向动态分配对象的指针向量中的元素之前,我需要做啥?

《C陷阱和缺陷》读书笔记-其二:语义陷阱

指针常量&常量指针&指向常量的指针常量