Арифметика указателей с двумя разными буферами - PullRequest
0 голосов
/ 28 января 2019

Рассмотрим следующий код:

int* p1 = new int[100];
int* p2 = new int[100];
const ptrdiff_t ptrDiff = p1 - p2;

int* p1_42 = &(p1[42]);
int* p2_42 = p1_42 + ptrDiff;

Теперь, гарантирует ли Стандарт, что p2_42 указывает на p2[42]?Если нет, то всегда ли это так в Windows, Linux или куче веб-сборок?

Ответы [ 4 ]

0 голосов
/ 29 января 2019

Стандарт допускает реализации на платформах, где память разделена на отдельные области, которые не могут быть достигнуты друг от друга с помощью арифметики указателей.В качестве простого примера, некоторые платформы используют 24-битные адреса, которые состоят из 8-битного номера банка и 16-битного адреса в банке.Добавление одного к адресу, который идентифицирует последний байт банка, даст указатель на первый байт этого того же самого банка, а не первый байт следующего банка.Этот подход позволяет вычислять адресную арифметику и смещения с использованием 16-битной математики, а не 24-битной математики, но требует, чтобы ни один объект не выходил за границы банка.Такая конструкция наложит некоторую дополнительную сложность на malloc и, вероятно, приведет к большей фрагментации памяти, чем в противном случае, но пользовательскому коду обычно не нужно заботиться о разделении памяти на банки.

Многие платформы не имеют таких архитектурных ограничений, а некоторые компиляторы, которые предназначены для низкоуровневого программирования на таких платформах, позволяют выполнять адресную арифметику между произвольными указателями.Стандарт отмечает, что распространенным способом лечения неопределенного поведения является «поведение во время перевода или выполнения программы документированным образом, характерным для среды», и поддержка обобщенной арифметики указателей в средах, поддерживающих его, хорошо вписывается в эту категорию.К сожалению, в стандарте не предусмотрены какие-либо средства для разграничения реализаций, которые ведут себя таким полезным образом, и тех, которые этого не делают.

0 голосов
/ 28 января 2019

Третья строка - Неопределенное поведение, поэтому стандарт допускает что-либо после этого.

Допустимо вычитать только два указателя, указывающие на (или после) один и тот же массив.

Windows или Linuxне очень актуальны;компиляторы и особенно их оптимизаторы - вот что ломает вашу программу.Например, оптимизатор может распознать, что p1 и p2 оба указывают на начало int[100], поэтому p1-p2 должно быть 0.

0 голосов
/ 28 января 2019

Чтобы добавить стандартную цитату:

expr.add # 5

Когда вычитаются два выражения указателя P и Q,тип результата - это определяемый реализацией знаковый целочисленный тип;этот тип должен быть того же типа, который определен как std::ptrdiff_­t в заголовке <cstddef> ([support.types]).

  • (5.1) Если P и Q оба значения равны нулю, указатель равен 0.

  • (5.2) В противном случае, если P и Q указывают соответственно на элементы x[i] и x[j] того же объекта массива x, выражение P - Q имеет значение i−j.

  • (5.3) В противном случае поведение не определено.[ Примечание: Если значение i−j не находится в диапазоне представимых значений типа std::ptrdiff_­t, поведение не определено.- примечание конца]

(5.1) не применяется, так как указатели не являются nullptr.(5.2) не применяется, потому что указатели не находятся в одном массиве.Итак, мы остались с (5.3) - UB.

0 голосов
/ 28 января 2019
const ptrdiff_t ptrDiff = p1 - p2;

Это неопределенное поведение.Вычитание между двумя указателями хорошо определено, только если они указывают на элементы в одном и том же массиве.( [expr.add] .35,3 ).

Когда вычитаются два выражения указателя P и Q, тип результата определяется подписью, определенной реализациейинтегральный тип;этот тип должен быть того же типа, который определен как std::ptrdiff_­t в заголовке <cstddef> ([support.types]).

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

И даже если был какой-то гипотетический способ получить это значение законным способом, даже такое суммирование является недопустимым,поскольку даже указатель + целочисленное суммирование ограничено, чтобы оставаться внутри границ массива ( [expr.add] ¶4,2 )

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

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