У меня вопрос о различиях указателей и результирующего типа, ptrdiff_t
.
C99 §6.5.6 (9) гласит:
Когда два указателя вычитаются, оба должны указывать на элементы одного и того же объекта массива или один за последним элементом объекта массива; результатом является разность индексов двух элементов массива. Размер результата определяется реализацией, а его тип (целочисленный тип со знаком)
ptrdiff_t
, определенный в заголовке. Если результат не представлен в объекте такого типа, поведение undefined. Другими словами, если выражения P и Q указывают соответственно i-ый и j-ый элементы объекта массива, выражение (P) - (Q) имеет значение i-j, если значение вписывается в объект типаptrdiff_t
.
В § 7.18.3 (2) требуется ptrdiff_t
иметь диапазон, по меньшей мере, [-65535, +65535]
Меня интересует поведение undefined, если результат слишком велик. Я не мог найти ничего в стандарте, гарантируя, по крайней мере, тот же диапазон, что и подписанная версия size_t
или что-то подобное. Итак, теперь вот мой вопрос: может ли соответствующая реализация сделать ptrdiff_t
подписанный 16-разрядный тип, но size_t
64 бит? Как указано в Guntram Blohm, 16-разрядная подписка составляет максимум 32767, поэтому мой пример явно не соответствует] Насколько я вижу, я не могу вычитание указателей на массивы с более чем 65535 элементами в строго соответствующем коде, даже если реализация поддерживает объекты, намного большие, чем это. Кроме того, программа может даже сбой.
Обоснование (V5.10) § 6.5.6 гласит:
Важно, чтобы этот тип [
ptrdiff_t
] был подписан, чтобы получить правильное алгебраическое упорядочение при работе с указателями внутри одного и того же массива. Однако величина разницы указателей может быть такой же, как размер самого большого объекта, который может быть объявлен; и поскольку это неподписанный тип, разница между двумя указателями может привести к переполнению некоторых реализаций.
который может объяснить, почему не требуется, чтобы каждая разница указателей (с элементами одного и того же массива) была определена, но не объясняет, почему ограничение PTRDIFF_MAX
не ограничено как минимум SIZE_MAX/2
(с целым делением).
Чтобы проиллюстрировать, предположим, что T
- это любой тип объекта и n
объект size_t
, неизвестный во время компиляции. Я хочу выделить пространство для n
объектов T
, и я хочу сделать вычитание указателя с адресами в выделенном диапазоне.
size_t half = sizeof(T)>1 ? 1 : 2; // (*)
if( SIZE_MAX/half/sizeof(T)<n ) /* do some error handling */;
size_t size = n * sizeof(T);
T *foo = malloc(size);
if(!foo) /* ... */;
не будет строго соответствовать, я должен был сделать
if( SIZE_MAX/sizeof(T) < n || PTRDIFF_MAX < n )
вместо этого. Неужели это так? И если да, то кто-нибудь знает причину этого (т.е. Не требует PTRDIFF_MAX >= SIZE_MAX/2
[изменить: изменено >
на >=
] или что-то подобное)?
(*) Полуфабрикат в первой версии - это то, что я узнал, когда писал этот текст, у меня был
if( SIZE_MAX/2/sizeof(T) < n )
сначала, взяв половину SIZE_MAX
для решения проблем, упомянутых в Обосновании; но тогда я понял, что нам нужно всего лишь половину SIZE_MAX
, если sizeof(T)
равно 1. Учитывая этот код, вторая версия (ту, которая, безусловно, строго соответствует), кажется, не так уж плоха. Но тем не менее, меня интересует, насколько я прав.
C11 содержит формулировку пунктов 6.5.6 (9), ответы на эту тему, связанные с С++, также приветствуются.