【问题标题】:Is it possible to get a pointer to one subobject via a pointer to a different, unreleated subobject?是否可以通过指向不同的、不相关的子对象的指针来获取指向一个子对象的指针?
【发布时间】:2020-01-18 14:22:34
【问题描述】:

看看这个简单的代码:

struct Point {
    int x;
    int y;
};

void something(int *);

int main() {
    Point p{1, 2};

    something(&p.x);

    return p.y;
}

我希望main 的返回值可以优化为return 2;,因为something 无法访问p.y,它只能获得指向p.x 的指针。

但是,没有一个主要的编译器将main 的返回值优化为2Godbolt.

如果我们只授予p.x 的访问权限,标准中是否有允许something 修改p.y 的内容?如果是,这是否取决于Point 是否有标准布局?

如果我改用something(&p.y);return p.x; 会怎样?

【问题讨论】:

  • 我不明白反对票和旗帜。这对我来说是完全合理的问题。
  • stackoverflow.com/questions/50803202/… "实现对齐要求可能会导致两个相邻的成员不能紧挨着分配"
  • @alterigel:这不重要,因为我们可以使用offsetof。但我不确定我们是否真的可以使用 C++17 的新指针语义规则从 p.x 获得指向 p.y 的指针。不能使用std::launder,因为指向p.x 的指针只能到达p.x。因此,即使我可以将指针移动到正确的位置,也可能该指针不会指向 p.y 的对象。
  • 如果将其更改为void something(int * x) { *(x+1) = 6; },它会将返回值优化为固定的6,因此编译器至少认为这是一个有效的选项。
  • @zereges,没有投反对票,但几乎投了。这个问题很难解析。我相信 OP 在问:“为什么编译器不能意识到 something() 函数不会修改 p.y ?”如果 OP 直接问这个问题,那么反对票就会减少。

标签: c++ pointers language-lawyer c++17 offsetof


【解决方案1】:

这是完美定义的:

void something(int *x) {
    reinterpret_cast<Point*>(x)->y = 42;
}

Point 对象 (p) 及其 x 成员是指针可互转换的,来自 [basic.compound]

两个对象 abpointer-interconvertible 如果:

  • [...]
  • 一个是标准布局类对象,另一个是该对象的第一个非静态数据成员,或者,如果该对象没有非静态数据成员,则该对象的任何基类子对象([class. mem]),或:
  • [...]

如果两个对象是指针可互转换的,那么它们具有相同的地址,并且可以通过reinterpret_­cast从指向另一个对象的指针中获得指向另一个对象的指针。

reinterpret_cast&lt;Point*&gt;(x) 是有效的,并且最终会得到一个指向 p 的指针。因此,直接修改它是可以的。如您所见,标准布局部分和第一个非静态数据成员部分很重要。


尽管如果您将指向 p.y 的指针传入并返回 p.x ,则问题中的编译器不会优化额外的负载。

【讨论】:

  • 谢谢,我知道我的问题留下了一个漏洞。那么,如果我使用something(&amp;p.y); return p.x;,那么显然是错过了优化?
  • @geza 从技术上讲,是的,那将是一个错过的优化,但我认为目前主要的编译器还没有准备好破坏所有使用 offsetof 的现有代码。
  • 这不违反严格的别名规则吗?还是这是一个仅限 C 的规则?
  • @sturcotte06 它不是因为 如果两个对象是指针可互转换的,那么它们具有相同的地址,并且可以通过指向另一个对象的指针获得指向另一个对象的指针reinterpret_cast。
  • 我在这里遗漏了什么吗?演员表将指向p.x 的指针更改为指向Point 的指针,但它仍然指向at 相同的内存位置?这意味着这仅有效,因为x 是第一个成员,因此存在于与“父”对象Point p 相同的内存位置?因此,如果我们将&amp;p.y 传递给函数,我们会得到不同的行为(未定义?)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-02-25
  • 2023-02-09
  • 2011-05-17
  • 2015-10-07
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多