Ptrdiff_t слишком мал?
Я всегда задавался вопросом: не означает ли ptrdiff_t
удержать разницу в двух указателях по определению? Как это происходит, когда два указателя слишком далеко? (Я не указываю на какой-либо конкретный язык... Я имею в виду все языки, которые имеют этот тип.)
(например, вычтите указатель с адресом 1
из указателя байта с адресом 0xFFFFFFFF
, когда у вас есть 32-битные указатели, и он переполнит бит знака...)
Ответы
Ответ 1
Нет, это не так.
$5.7 [expr.add] (от n3225 - С++ 0x FCD)
Когда два указателя на элементы одного и того же объекта массива вычитаются, результатом является разность индексов двух элементов массива. Тип результата - это определенный тип интегрированного интегрального типа; этот тип должен быть того же типа, что и std::ptrdiff_t
в заголовке <cstddef>
(18.2). Как и при любом другом арифметическом переполнении, если результат не соответствует указанному пространству, поведение undefined. Другими словами, если выражения P
и Q
указывают соответственно на i
-th и j
-th элементы объекта массива, выражение (P)-(Q)
имеет значение i − j
, если значение подходит для объекта типа std::ptrdiff_t
. Более того, если выражение P
указывает либо на элемент объекта массива, либо мимо последнего элемента объекта массива, а выражение Q
указывает на последний элемент одного и того же объекта массива, выражение ((Q)+1)-(P)
имеет такое же значение, как ((Q)-(P))+1
и как -((P)-((Q)+1))
, и имеет значение 0, если выражение P
указывает один за последним элементом объекта массива, хотя выражение (Q)+1
не указывает на элемент объект массива. Если оба указателя не указывают на элементы одного и того же объекта массива или один за последним элементом объекта массива, поведение undefined.
Обратите внимание, что в абзаце появляется количество раз undefined
. Также обратите внимание, что вы можете только вычитать указатели, если они указывают внутри одного и того же объекта.
Ответ 2
Нет, потому что нет никакой разницы между "любыми двумя указателями". Вы можете только вычитать указатели на элементы одного и того же массива (или указатель на местоположение, расположенное за концом массива).
Ответ 3
Чтобы добавить более явную стандартную цитату, ISO 9899:1999 §J.2/1
заявляет:
Поведение undefined в следующих случаях:
[...]
- Результат вычитания двух указателей не представляется в объекте типа ptrdiff_t (6.5.6).
Ответ 4
Для ptrdiff_t
вполне допустимо, чтобы он был того же размера, что и типы указателей, если семантика переполнения определена компилятором, так что любая разница все еще представляется. Нет никакой гарантии, что отрицательный ptrdiff_t означает, что второй указатель живет с более низким адресом в памяти, чем первый, или что ptrdiff_t подписан вообще.
Ответ 5
Over/underflow математически хорошо определен для целочисленной арифметики фиксированного размера:
(1 - 0xFFFFFFFF) % (1<<32) =
(1 + -0xFFFFFFFF) % (1<<32) =
1 + (-0xFFFFFFFF % (1<<32)) = 2
Это правильный результат!
В частности, результат после over/underflow является псевдонимом правильного целого. На самом деле, каждое не представимое целое число является псевдонимом (неразличимым) с одним представляемым целым числом - счетчиком до бесконечности в целых числах фиксированного размера, и вы будете повторяться, круглые и круглые, как набор аналоговых часов.
N-разрядное целое представляет любое действительное целое число по модулю 2 ^ N. В C по модулю 2 ^ N записывается как% (1 < 32).
Я считаю, что C гарантирует математическую корректность over/underflow, но только для целых чисел без знака. Предполагается, что под/переполнением никогда не будет (ради оптимизации).
На практике целые числа со знаком являются двумя дополнениями, что не делает различий в добавлении или вычитании, поэтому правильное поведение при переполнении также гарантируется для целых чисел со знаком (хотя и не с помощью C).