Можно ли получить указатель на один подобъект через указатель на другой, не связанный объект?

Посмотрите на этот простой код:

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 до 2. Godbolt.

Есть ли в стандарте что-то, что позволяет something модифицировать p.y, если мы только даем доступ к p.x? Если да, зависит ли это от того, имеет ли Point стандартную компоновку?

Что если я использую something(&p.y); и return p.x; вместо этого?

Ответы

Ответ 1

Это совершенно четко определено:

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

Объект Point (p) и его член x являются взаимозаменяемыми по указателю, из [basic.compound]:

Два объекта a и b являются взаимозаменяемыми по указателю, если:

  • [...]
  • один является объектом класса стандартной компоновки, а другой - первым нестатическим элементом данных этого объекта или, если у объекта нет нестатических членов данных, любой подобъект базового класса этого объекта ([class.mem]) или:
  • [...]

Если два объекта являются взаимозаменяемыми по указателю, то они имеют один и тот же адрес, и можно получить указатель на один из указателя на другой через reinterpret_­cast.

Этот reinterpret_cast<Point*>(x) является действительным и в конце концов имеет указатель, который указывает на p. Следовательно, изменение его напрямую - это хорошо. Как видите, часть стандартного макета и первая часть не статических данных имеют большое значение.


Хотя это не так, как рассматриваемые компиляторы оптимизируют дополнительную нагрузку, если вы передаете указатель на p.y и возвращаете p.x.