T * по сравнению с char * арифметикой указателя

Предположим, что у нас есть массив, содержащий N элементов типа T.

T a[N];

Согласно стандарту С++ 14, при каких условиях у нас есть гарантия, что

 (char*)(void*)&a[0] + n*sizeof(T) == (char*)(void*)&a[n],  (0<=n<N) ?

Хотя это верно для многих типов и реализаций, стандарт упоминает его в сноске и двусмысленно:

§5.7.6, сноска 85) Еще один способ приблизиться к арифметике указателя...

Существует мало признаков того, что этот другой способ считался эквивалентным стандартным образом. Возможно, это будет намек для разработчиков, который предлагает одну из многих соответствующих реализаций.


Изменения:

Люди недооценили трудность этого вопроса.

Этот вопрос не о том, что вы можете прочитать в учебниках, а о том, что вы можете вывести из С++ 14 Standard с помощью логики и разума.

Если вы используете "смежный" или "смежный", пожалуйста, также скажите, что смежно.

В то время как T [] и T * тесно связаны, они являются абстракциями, а добавление на T * x N может быть определено реализацией любым последовательным способом.

Уравнение было перестроено с использованием добавления указателя. Если p указывает на a char, p + 1 всегда определяется с помощью (§5.7 (4)) или унарного сложения, поэтому мы не сталкиваемся с UB. Оригинал включал вычитание указателя, которое могло вызвать UB на ранней стадии. (Указатели char сравниваются, а не разыменовываются).

Ответ 1

В [dcl.array]:

Объект типа массива содержит смежно выделенную непустую набор N подобъектов типа T.

Смежный означает, что смещение между любыми последовательными подобъектами типа T равно sizeof(T), что означает, что смещение объекта N th <<25 > .

Верхняя граница n < N исходит из [expr.add]:

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

Ответ 2

Это всегда верно, но вместо того, чтобы смотреть на правила для арифметики указателя, вы должны полагаться на семантику, данную для оператора sizeof (5.3.3 [expr.sizeof]):

При применении к эталонному или ссылочному типу, результатом является размер ссылочного типа. При применении к классу результатом является количество байтов в объекте этого класса, включая любое дополнение, необходимое для размещения объектов этого типа в массиве. Размер самого производного класса должен быть больше чем ноль. Результатом применения sizeof к подобъекту базового класса является размер типа базового класса. При применении к массиву результатом является общее количество байтов в массиве. Это означает, что размер массива из n элементов в n раз превышает размер элемента.

Должно быть ясно, что существует только одна упаковка, которая помещает n неперекрывающихся элементов в пространство n * sizeof(element), а именно, что они разнесены на несколько интервалов sizeof (element). И только один порядок допускается с помощью правил сравнения указателей, найденных в разделе реляционного оператора (5.9 [expr.rel]):

Сравнение указателей с объектами определяется следующим образом:

  • Если два указателя указывают на разные элементы одного и того же массива или на его подобъекты, указатель на элемент с более высоким индексом сравнивается больше.

Ответ 3

Объявление в первой строке также является определением. (П .3.1 (2)) Он создает объект массива. (Пункте 1.8 (1))

Доступ к объекту возможен через несколько значений lvalues из-за правил псевдонимов. (§3.10 (10)) В частности, объекты на правая сторона может быть законно доступна (сглажена) с помощью указателей char.

Давайте посмотрим на предложение в определении массива, а затем устраним его неоднозначность.

"Объект типа массива содержит смежно выделенный непустой набор из N подобъектов типа T." [dcl.array] §8.3.4.


Disambiguation

Мы начинаем с двоичного симметричного отношения "смежные" для объектов char, что должно быть очевидно. ('iff' является коротким для "тогда и только тогда", множества и последовательности являются математическими, а не контейнерами С++). Если вы можете ссылку на лучшее или более признанное определение, комментарий.

Последовательность x_1... x_N объектов char является смежной, если f x_i и x_ {i + 1} смежны в памяти для всех я = 1... N-1.

Множество M объектов char является смежным, если объекты в M можно пронумеровать, x_1... x_N, скажем так, что последовательность (x_i) _i смежна. То есть, если M - образ смежной инъективной последовательности.

Два множества M_1, M_2 объектов char смежны, если существуют x_1 в M_1 и x_2 в M_2 такие, что x_1 и x_2 смежны.

Последовательность M_1... M_N множеств объектов char смежна, если f M_i и M_ {i + 1} смежны для всех я = 1... N-1.

Множество наборов объектов char является смежным, если оно является образом последовательная инъективная последовательность множеств объектов char.

Теперь, какую версию "смежных" применять? Разрешение лингвистической перегрузки:

1) "смежный" может относиться к "распределению". Поскольку вызов функции распределения обеспечивает подмножество доступных объектов char, это вызовет вариант набора символов. То есть, набор всех объектов char, которые встречаются в любом из N подобъектов, будет означать непрерывность.

2) "смежный" может относиться к "множеству". Это вызовет вариант с множеством наборов символов с каждым подобъектом, рассматриваемым как набор объектов char.


Что это значит? Во-первых, хотя авторы пронумеровали подобъекты массивов a [0]... a [N-1], они решили ничего не говорить о порядок подобъектов в памяти: они использовали "set" вместо "sequence". Они описали распределение как смежное, но они не говорят, что a [j] и a [j + 1] смежны в памяти. Кроме того, они решили не записывать прямая формула с указателями (char *) и sizeof(). Хотя похоже, что они преднамеренно отделили соприкосновение от проблем порядка, В §5.9 (3) требуется одно и то же упорядочение для подобъектов массивов всех типов.

Если указатели указывают на два разных элемента одного и того же массива или его подобъект, указатель к элементу с более высоким индексом больше.

Теперь байты, составляющие подобъекты массива, квалифицируются как подобъектов в смысле приведенной выше цитаты? Чтение §1.8 (2) и Завершить объект или подобъект? ответ: Нет, по крайней мере, не для массивов, элементы которых не содержат подобъектов и не являются массивами символов, например. массивы ints. Таким образом, мы можем найти примеры, когда на элементы массива не накладывается определенное упорядочение.

Но на данный момент допустим, что наши подобъекты массива заполнены только символами. Что это значит, учитывая две возможные интерпретации "смежных"?

1) Мы имеем непрерывное множество байтов, которое совпадает с упорядоченным набором подобъектов. Тогда утверждение в OP безоговорочно верно.

2) Мы имеем непрерывную последовательность подобъектов, каждая из которых может быть несмежной индивидуально. Это может произойти двумя способами: либо подобъекты могут иметь пробелы, то есть они содержат два объекта char на расстоянии, превышающем sizeof (подобъект) -1. Или подобъекты могут быть распределены между различными последовательностями смежных байтов.

В случае 2) нет гарантии, что требование в OP истинно.

Поэтому важно четко понимать, что означает "смежный".


Наконец, здесь приведен пример реализации, когда на подобъекты массива в §5.9 наложено очевидное упорядочение, поскольку подобъекты массива сами не имеют подобъектов. Читатели высказывали опасения, что это будет противоречить стандарту в других местах, но пока не было продемонстрировано определенное противоречие.

Предположим, что T является int, и у нас есть одна конкретная соответствующая реализация, которая ведет себя как ожидалось наивно с одним исключением:

Он выделяет массивы int в обратном порядке памяти, поместив первый элемент массива в конец адреса объекта с высокой памятью:

a[N-1], a[N-2], ... a[0]  

вместо

a[0], a[1],   ... a[N-1]  

Эта реализация удовлетворяет любой разумной смежности требование, поэтому нам не нужно соглашаться на одну интерпретацию "смежный", чтобы продолжить аргумент.

Тогда, если p указывает на a, отображение p в & a [0] (с вызовом [conv.array]) заставит прыжок указателя вблизи верхнего конца памяти a. Поскольку арифметика массива должна быть совместима с арифметикой указателя, у нас также есть

int * p= &intVariable;
(char*)(p+1) + sizeof(int) == (char*)p

и

int a[N];

(char*)(void*)&a[n] + n*sizeof(int)==(char*)(void*)&a[0],  (0<=n<N)

Тогда для T = int нет гарантии, что утверждение в исходном сообщении истинно.


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