Арифметика указателя внутри структуры

Внутри массива разрешена арифметика указателя (пока результат не выходит за границы, где конечная граница находится за концом массива).

// Legit
int m[3] = {0, 1, 2};
int const *p = m;
std::cout << *p << *(p + 1) << *(p + 2) << std::endl;

Разрешает ли стандарт подобную арифметику указателя со стандартной структурой макета, которая содержит последовательные переменные-члены одного и того же фундаментального типа?

// Well defined???
struct MyStruct {
    int a, b, c;
};
static_assert(std::is_standard_layout_v<MyStruct>);

MyStruct  m = {0, 1, 2};
int const *p = &m.a;
std::cout << *p << *(p + 1) << *(p + 2) << std::endl;

Я, конечно, могу сравнивать указатели на отдельные переменные (т. е. &m.a < &m.b), потому что они являются членами одного и того же объекта, но я не могу определить, гарантированно ли &m.a+1 будет &m.b.

Я знаю, что это, вероятно, будет работать с большинством компиляторов, но мне любопытно, что говорит стандарт (C++ 20, если это имеет значение).


person Adrian McCarthy    schedule 24.06.2021    source источник
comment
Я не верю, что стандарт гарантирует, что это сработает, потому что компилятор может добавлять отступы между членами данных по таким причинам, как выравнивание. Но я не знаю, есть ли какая-то конкретная формулировка, в которой говорится, что это не гарантирует работу.   -  person templatetypedef    schedule 24.06.2021
comment
... Указатель на объект, не являющийся массивом, рассматривается как указатель на первый элемент массива размером 1.... en.cppreference.com/w/cpp/language/operator_arithmetic и, как вы знаете, разыменование 1 после конца (или больше) является неопределенным поведением.   -  person Richard Critten    schedule 24.06.2021
comment
...Я, конечно, могу сравнивать указатели... нет, вы можете сравнивать только указатели, указывающие на один и тот же объект m.a и m.b - это разные объекты. Однако вы можете использовать std::less для сравнения указателей, а не встроенный operator<   -  person Richard Critten    schedule 24.06.2021
comment
Нет, такого требования нет. Если вы хотите рассматривать элементы объекта как элементы массива, поместите их в массив.   -  person Pete Becker    schedule 24.06.2021
comment
Не stackoverflow.com/ вопросов/58428562/ ответить на ваш вопрос?   -  person DL33    schedule 24.06.2021
comment
Интересно... Если бы вы поместили union в структуру... или использовали pragma pack, изменился бы ответ?   -  person Matt    schedule 24.06.2021
comment
@RichardCritten: Спасибо за этот улов. Меня обманул поисковик. Я запросил реляционные операторы указателя С++, и лучший результат был для C, а не для C++, и эта гарантия действительно существует в C.   -  person Adrian McCarthy    schedule 24.06.2021
comment
@DL33: Да, спасибо. Я не нашел этого в своих веб-поисковиках. И окно похожих вопросов SO, казалось, думало, что мой вопрос был о PHP, хотя я пометил его как C++.   -  person Adrian McCarthy    schedule 24.06.2021


Ответы (1)


Из раздела Additive operators

(выделено мной)

Когда выражение J, имеющее целочисленный тип, добавляется или вычитается из выражения P типа указателя, результат имеет тип P.

  • Если P оценивается как значение нулевого указателя, а J оценивается как 0, результатом является значение нулевого указателя.
  • В противном случае, если P указывает на элемент массива i объекта массива x с n элементами, выражения P + J и J + P (где J имеет значение j) указывают на (возможно, гипотетический) элемент массива i + j [...]
  • В противном случае поведение не определено.

В итоге нет никаких гарантий, если базовый тип не является массивом.

По вашему мнению, std::is_standard_layout_v предоставляет гарантии в отношении самого макета, которые могут включать гарантии для содержащихся в нем массивов, но не квалифицирует сам struct как массив элементов, который стандарт предполагает использование арифметики указателя.

person Adrien Leravat    schedule 24.06.2021
comment
Обратите внимание, что это также запрещает &m.a < &m.b. - person bitmask; 24.06.2021
comment
@bitmask О да?! Разве члены структур/классов не должны располагаться в порядке объявления? ... ;) (Я был бы удивлен, что это выражение не гарантирует истинность...) (особенно для стандартных типов макетов) Я ошибаюсь? И если да, то почему? Пожалуйста... - person Tenphase; 24.06.2021
comment
@Tenphase Два разных объекта. Не имеет значения, что они являются членами третьего объекта, не являющегося массивом. - person bitmask; 24.06.2021
comment
@bitmask Нет, это разрешено. Из expr.rel Если два указателя указывают на разные не- статические элементы данных одного и того же объекта, ..., указатель на более поздний объявленный элемент требуется для сравнения больше, если два элемента имеют одинаковый контроль доступа... - person 1201ProgramAlarm; 24.06.2021
comment
@bitmask Я немного запутался в этом, должен сказать ... Если стандартный тип макета тривиально копируется (его можно скопировать через memcpy), а элементы располагаются в порядке объявления, адрес a в struct m должен быть ниже адреса b в том же объекте... Нет?! :о - person Tenphase; 24.06.2021
comment
@1201ProgramAlarm Это меня успокаивает... :P Иначе как бы это было возможно memcpy ? Как можно создавать межъязыковые API... ;) – (что я и делал довольно часто...) - person Tenphase; 24.06.2021
comment
@ 1201ProgramAlarm Я исправлен. - person bitmask; 24.06.2021