Соответствующие правила из последнего стандартного черновика:
[intro.object]
Объекты могут содержать другие объекты, называемые подобъектами. Субобъект может быть подобъектом-членом ([class.mem]), подобъектом базового класса ([class.derived]) или элементом массива. Объект, который не является подобъектом любого другого объекта, называется законченным объектом.
Итак, элементы массива являются подобъектами. Массив в этом примере является законченным объектом.
[expr.eq]
... Сравнение указателей определяется следующим образом:
Если один указатель представляет адрес завершенного объекта, а другой указатель представляет адрес после последнего элемента другого завершенного объекта, 79 результат сравнения не определен.
В противном случае, если ... оба представляют один и тот же адрес, они сравниваются равными.
79) Как указано в [ basi c .compound], объект, который не является элементом массива, считается для этой цели принадлежащим одноэлементному массиву
Первый случай почти совпадает, но не совсем. Оба являются указателями на один за последним элементом другого завершенного объекта - один завершенный объект - это массив arr
, другой - гипотетический одноэлементный массив, элемент которого равен arr
. И ни один из них не является указателем на несвязанный завершенный объект, который может существовать после их соответствующего массива, как пояснено в примечании в следующей цитате [basi c .compound].
Таким образом, другой случай должен применяться при условии, что они представляют один и тот же адрес.
[basi c .compound]
Значение типа указателя, которое является указателем на конец объекта или после него представляет адрес первого байта в памяти ([intro.memory]), занятого объектом 43 или первого байта в памяти после окончания памяти, занимаемой объектом, соответственно. [Примечание: указатель за концом объекта ([expr.add]), как полагают, не указывает на несвязанный объект типа объекта, который может быть расположен по этому адресу. Значение указателя становится недействительным, когда хранилище, которое он обозначает, достигает конца своего срока хранения; см. [basi c .stc]. - примечание к концу]
Для целей ... сравнения ([expr.rel], [expr.eq]), a указатель за концом последнего элемента массива x из n элементов считается эквивалентным указателю на гипотетический элемент массива n с x и объектом типа T, который не является элементом массива, считается принадлежащим массиву с одним элементом типа T . ...
43) Для объекта, который не находится в пределах его времени жизни, это первый байт в памяти, который он будет занимать или занимать.
И arr
, и гипотетический массив, содержащий всего arr
, имеют одинаковый адрес и одинаковый размер. Поэтому один элемент после обоих массивов считается эквивалентным указателю по тому же адресу за пределами массива.
Просто чтобы было ясно, что массив не может содержать отступы:
[expr.sizeof]
... При применении к классу результатом является количество байтов в объекте этого класса, включая любые отступы, необходимые для размещения объектов этого типа в массиве. , Результатом применения sizeof к потенциально перекрывающемуся подобъекту является размер типа, а не размер подобъекта. При применении к массиву результатом является общее количество байтов в массиве. Это означает, что размер массива из n элементов в n раз больше размера элемента.
Уточним, что преобразование в void*
не должно изменять значение указателя и, следовательно, равенство .
[conv.ptr]
Значение типа «указатель на cv T», где T - тип объекта, может быть преобразовано в значение типа «указатель на cv void». Значение указателя ([basi c .compound]) не изменяется при этом преобразовании.