Я вижу несколько сообщений (таких как 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 по определению являются «положительными»)]Спасибо!
Во-первых, понятно не то, что 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
,
Смотрите также,