почему sizeof (ptrdiff_t) == sizeof (uintptr_t)

Я вижу несколько сообщений (таких как size_t против uintptr_t) о size_t и uintptr_t / ptrdiff_t, но не об относительных размерах этих новых типов размеров c99 ptr.

пример машины: ванильный убунту 14lts x64, gcc 4.8:

printf("%zu, %zu, %zu\n", sizeof(uintptr_t), sizeof(intptr_t), sizeof(ptrdiff_t));

печать: «8, 8, 8»

это не имеет смысла для меня, так как я ожидаю, что тип diff, который должен быть подписан, потребует больше битов, чем сам ptr без знака.

рассматривать:

NULL - (2^64-1)  /*largest ptr, 64bits of 1's.*/

который, будучи отрицательным дополнением 2, не поместился бы в 64 бита; следовательно, я ожидаю, что ptrdiff_t будет больше, чем ptr_t.

[связанный вопрос, почему intptr_t имеет тот же размер, что и uintptr_t …. хотя мне было удобно, возможно, это было просто для того, чтобы подписанный тип мог содержать биты представления (например, использование арифметики со знаком на отрицательном ptr будет (а) неопределенный, и (b) имеет ограниченную полезность, так как ptrs по определению являются «положительными»)]

Спасибо!

5

Решение

Во-первых, понятно не то, что uintptr_t здесь делают Языки (C и C ++) не позволяют вычитать любые произвольные значения указателей друг от друга. Два указателя могут быть вычтены, только если они указывают на один и тот же объект (на один и тот же объект). массив объект). В противном случае поведение не определено. Это означает, что эти два указателя не могут быть дальше, чем SIZE_MAX байт друг от друга. Примечание: расстояние ограничено диапазоном size_tне по диапазону uintptr_t, В общем случае uintptr_t может быть большего типа, чем size_t, Никто в C / C ++ никогда не обещал вам, что вы сможете вычесть два указателя, расположенных UINTPTR_MAX байт друг от друга.

(И да, я знаю, что на платформах с плоской памятью uintptr_t а также size_t обычно одного типа, по крайней мере, по дальности и представлению. Но с языковой точки зрения неверно полагать, что они всегда есть.)

Ваш NULL - (2^64-1) (если интерпретировать как вычитание адреса) является ярким примером такого сомнительного вычитания. Что заставило вас думать, что вы должны быть в состоянии сделать это в первую очередь?

Во-вторых, после переключения с неактуального uintptr_t гораздо более актуальным size_tМожно сказать, что ваша логика совершенно верна. sizeof(ptrdiff_t) должно быть больше чем sizeof(size_t) из-за дополнительного бита, необходимого для представления подписанного результата. Тем не менее, как бы странно это ни звучало, языковая спецификация не требует ptrdiff_t быть достаточно широким, чтобы вместить все результаты вычитания указателя, даже если два указателя указывают на части одного и того же объекта (т.е. они не дальше, чем SIZE_MAX кроме байтов). ptrdiff_t юридически разрешено иметь тот же счетчик битов, что и size_t,

Это означает, что «кажущееся правильным» вычитание указателя может фактически привести к неопределенному поведению просто потому, что результат слишком велик. Если ваша реализация позволяет вам объявить char массив размера, скажем, SIZE_MAX / 3 * 2

char array[SIZE_MAX / 3 * 2]; // This is smaller than `SIZE_MAX`

затем вычитание совершенно корректных указателей в конец и в начало этого массива может привести к неопределенному поведению, если ptrdiff_t имеет тот же размер, что и size_t

char *b = array;
char *e = array + sizeof array;

ptrdiff_t distance = e - b; // Undefined behavior!

Авторы этих языков решили выбрать это более простое решение вместо того, чтобы требовать от компиляторов реализации поддержки [вероятно, не нативного] целочисленного типа со знаком со всей шириной знака. ptrdiff_t,

Реальные реализации знают об этой потенциальной проблеме и обычно предпринимают шаги, чтобы ее избежать. Они искусственно ограничивают размер самого большого поддерживаемого объекта, чтобы вычитание указателя никогда не переполнялось. В типичной реализации вы не сможете объявить массив больше чем PTRDIFF_MAX байт (что о SIZE_MAX / 2). Например. даже если SIZE_MAX на вашей платформе 264-1, реализация не позволит вам объявить что-то большее, чем 263-1 байт (и реальные ограничения, связанные с другими факторами, могут быть даже более жесткими). С этим ограничением любое законное вычитание указателя даст результат, который вписывается в диапазон ptrdiff_t,

Смотрите также,

16

Другие решения


По вопросам рекламы ammmcru@yandex.ru
Adblock
detector