В моей системе и ptrdiff_t
, и size_t
имеют 64-битный.
Я хотел бы прояснить две вещи:
Я считаю, что ни один массив не может быть таким большим, как size_t
, из-за ограничений адресного пространства. Это правда?
Если да, то есть ли гарантия, что ptrdiff_t
сможет удерживать результат вычитания указателей любой в массиве максимального размера?
Для ptrdiff_t
"переполнение" размер объекта должен быть 1 (размер символа). Для размера объекта> 2 все в порядке.
@RobertAndrzejuk, извини, если я недостаточно ясно понял. Я имел в виду: «указатели на элементы массива».
Я также хотел обратить ваше внимание (используя кавычки @NathanOliver), так это то, что при определении long long arr[2]
вычитание &arr[1] - &arr[0]
дает результат 1
(а не 4
, как можно было бы ожидать). Итак, чтобы получить эту проблему "переполнения", вы должны иметь дело с необработанной памятью (типы размером 1 байт).
@RobertAndrzejuk, спасибо, что указали на это!
@RobertAndrzejuk Замечание. Можно было бы ожидать, что результат вычитания из вашего примера будет 8, а не 4.
Нет, такой гарантии нет. См., Например, здесь: https://en.cppreference.com/w/cpp/types/ptrdiff_t
If an array is so large (greater than PTRDIFF_MAX elements, but less than SIZE_MAX bytes), that the difference between two pointers may not be representable as std::ptrdiff_t, the result of subtracting two such pointers is undefined.
Большинство реализаций искусственно ограничивают максимальный размер массива, чтобы убедиться, что разница между двумя указателями, указывающими на один и тот же массив, соответствует ptrdiff_t
. Таким образом, более чем вероятно, что на вашей платформе максимально допустимый размер массива составляет около SIZE_MAX / 2
(попробуйте). Это не «ограничение адресного пространства», это просто внутреннее ограничение вашей реализации. В соответствии с этим ограничением вычитание допустимого указателя («допустимый» = два указателя в одном массиве) не приведет к переполнению.
Однако спецификация языка не требует этого. Реализации не обязаны таким образом ограничивать размер своих массивов, а это означает, что спецификация языка позволяет, казалось бы, законным вычитаниям указателей переполняться и приводить к неопределенному поведению. Но большинство реализаций предпочитают защищаться от этого, ограничивая размеры своих массивов.
Для получения более подробной информации см. «Три варианта» здесь: Почему максимальный размер массива «слишком велик»?
Верно! Компиляция следующего кода: char array[SIZE_MAX/2+1];
приводит к ошибке: размер массива «array» слишком велик
Из [support.types.layout] / 3
The type
size_t
is an implementation-defined unsigned integer type that is large enough to contain the size in bytes of any object.
Таким образом, вам гарантировано, что size_t
может содержать самый большой массив, который вы можете иметь.
ptrdiff_t
, к сожалению, не имеет такой гарантии. Из [support.types.layout] / 2
The type
ptrdiff_t
is an implementation-defined signed integer type that can hold the difference of two subscripts in an array object, as described in 8.7.
Это нормально, но тогда у нас есть [expr.add] / 5
When two pointers to elements of the same array object are subtracted, the type of the result is an implementation-defined signed integral type; this type shall be the same type that is defined as std::ptrdiff_t in the header (21.2). If the expressions P and Q point to, respectively, elements x[i] and x[j] of the same array object x, the expression P - Q has the value i − j; otherwise, the behavior is undefined. [ Note: If the value i − j is not in the range of representable values of type std::ptrdiff_t, the behavior is undefined. —end note ]
Что говорит о том, что ptrdiff_t
может быть недостаточно большим.
Не различия указателей, а результат вычитания индекса.