Стандарт C гарантирует, что size_t
это тип, который может содержать любой индекс массива. Это означает, что, по логике, size_t
должен быть в состоянии содержать любой тип указателя. Я читал на некоторых сайтах, которые я нашел на Google, что это законно и / или должно всегда работать:
void *v = malloc(10);
size_t s = (size_t) v;
Итак , в C99, стандарт введены intptr_t
и uintptr_t
типы, которые подписали и беззнаковых типов гарантированно быть в состоянии держать указатели:
uintptr_t p = (size_t) v;
Так в чем же разница между использованием size_t
и uintptr_t
? Оба без знака, и оба должны быть в состоянии содержать любой тип указателя, поэтому они кажутся функционально идентичными. Есть ли реальная веская причина для использования uintptr_t
(или, что еще лучше, a void *
), а не a size_t
, кроме ясности? В непрозрачной структуре, где поле будет обрабатываться только внутренними функциями, есть ли причина не делать этого?
Точно так же ptrdiff_t
был подписанный тип, способный содержать различия в указателях и, следовательно, способный удерживать практически любой указатель, так чем он отличается от intptr_t
?
Разве все эти типы не обслуживают тривиально разные версии одной и той же функции? Если нет, то почему? Что я не могу сделать с одним из них, что я не могу сделать с другим? Если так, то почему C99 добавил в язык два по существу лишних типа?
Я готов не обращать внимания на указатели функций, поскольку они не относятся к текущей проблеме, но не стесняйтесь упоминать их, так как у меня есть скрытое подозрение, что они будут центральными для «правильного» ответа.
size_t
и ,uintptr_t
кроме того, что оptrdiff_t
иintptr_t
- не оба из них будет иметь возможность хранить один и тот же диапазон значений почти на любой платформе? Почему существуют целочисленные типы как со знаком, так и без знака, особенно если ониptrdiff_t
уже предназначены для целочисленного типа со значением со знаком.size_t
должен быть не менее 16 бит, ноptrdiff_t
не менее 17 бит (что на практике означает, что он, вероятно, будет не менее 32 бит).SIZE_MAX
что он не должен быть 2 ** 64. Имейте в виду, что используется плоская адресация; Сегментация не требуется для того, чтобы иметь несоответствие междуSIZE_MAX
диапазоном указателя данных.По поводу вашего заявления:
Это на самом деле заблуждение (заблуждение из-за неправильных рассуждений) (а) . Вы можете подумать, что последнее следует из первого, но на самом деле это не так.
Указатели и индексы массивов - это не одно и то же. Вполне правдоподобно предусмотреть соответствующую реализацию, которая ограничивает массивы 65536 элементами, но позволяет указателям обращаться к любому значению в массивном 128-битном адресном пространстве.
C99 утверждает, что верхний предел
size_t
переменной определяется,SIZE_MAX
и это может быть как 65535 (см. C99 TR3, 7.18.3, без изменений в C11). Указатели были бы довольно ограничены, если бы они были ограничены этим диапазоном в современных системах.На практике вы, вероятно, обнаружите, что ваше предположение верно, но это не потому, что стандарт гарантирует это. Потому что на самом деле это не гарантирует.
(а) Кстати, это не какая-то личная атака, а просто заявление о том, почему ваши высказывания ошибочны в контексте критического мышления. Например, следующие рассуждения также недействительны:
Симпатичность или иное отношение к щенкам здесь не имеет отношения, все, что я утверждаю, это то, что два факта не приводят к выводу, потому что первые два предложения допускают существование милых вещей, которые не являются щенками.
Это похоже на ваше первое утверждение, не обязательно обязательное второе.
источник
ptrdiff_t
противintptr_t
).Я позволю всем остальным ответам отстоять себя относительно рассуждений с ограничениями сегментов, экзотических архитектур и так далее.
Разве простой разницы в именах недостаточно, чтобы использовать правильный тип для правильной вещи?
Если вы храните размер, используйте
size_t
. Если вы храните указатель, используйтеintptr_t
. Человек, читающий ваш код, мгновенно узнает, что «ага, это размер чего-то, возможно, в байтах», и «о, вот значение указателя, по какой-то причине сохраняемое как целое число».В противном случае, вы можете просто использовать
unsigned long
(или, в наши современные временаunsigned long long
) для всего. Размер - это еще не все, имена типов несут смысл, что полезно, поскольку помогает описать программу.источник
size_t
поле.void*
,intptr_t
иuintptr_t
гарантированно иметь возможность представлять какой - либо указатель на данные.Возможно, размер самого большого массива меньше, чем указатель. Подумайте о сегментированных архитектурах - указатели могут быть 32-разрядными, но один сегмент может быть способен адресовать только 64 КБ (например, старая архитектура 8086 реального режима).
Хотя они обычно не используются на настольных компьютерах, стандарт C предназначен для поддержки даже небольших специализированных архитектур. Например, все еще разрабатываются встроенные системы с 8- или 16-битными процессорами.
источник
size_t
также должны быть в состоянии справиться с этим? Или динамические массивы в некотором удаленном сегменте все еще будут ограничены индексированием в пределах их сегмента?str
функций и Борланд даже дляmem
функций (memset
,memcpy
,memmove
). Это означало, что при переполнении смещения вы могли перезаписывать часть памяти, что было интересно отлаживать на нашей встроенной платформе.Я хотел бы представить (и это относится ко всем именам типов), что он лучше передает ваши намерения в коде.
Например, даже если
unsigned short
иwchar_t
имеют тот же размер на Windows (я думаю), используяwchar_t
вместоunsigned short
показывает намерение , что вы будете использовать его для хранения широкого характера, а не просто каким - то произвольное числа.источник
wchar_t
она намного больше, чемunsigned short
использование одной для другой, будет ошибочным и создаст серьезную (и современную) проблему переносимости, в то время как проблемы переносимости междуsize_t
и,uintptr_t
кажется, лежат в отдаленных странах. что-то 1980-го (случайный удар в темноте на свидание, там)size_t
иuintptr_t
до сих пор подразумевается использование в их именах.Оглядываясь назад и вперед, и вспоминая, что различные странные архитектуры были разбросаны по ландшафту, я почти уверен, что они пытались обернуть все существующие системы, а также предусмотреть все возможные будущие системы.
Так что, как бы все ни было улажено, нам пока нужно не так много типов.
Но даже в LP64, довольно распространенной парадигме, нам нужны size_t и ssize_t для интерфейса системных вызовов. Можно представить себе более ограниченную унаследованную или будущую систему, где использование полного 64-разрядного типа обходится дорого, и они могут захотеть использовать операции ввода-вывода объемом более 4 ГБ, но при этом все еще имеют 64-разрядные указатели.
Я думаю, вы должны задаться вопросом: что могло бы быть разработано, что может произойти в будущем. (Возможно, 128-битные указатели для распределенной системы через Интернет, но не более 64 бит в системном вызове, или, возможно, даже «устаревшее» 32-битное ограничение. :-) Представьте, что унаследованные системы могут получить новые компиляторы C .. ,
Кроме того, посмотрите на то, что существовало тогда. Помимо моделей памяти реального времени zillion 286, как насчет мэйнфреймов CDC с 60-битным словом / 18-битным указателем? Как насчет серии Cray? Не берите в голову нормальные ILP64, LP64, LLP64. (Я всегда думал, что Microsoft - это претенциозно с LLP64, это должен был быть P64.) Я, конечно, могу представить комитет, пытающийся охватить все основы ...
источник
Подразумевается, что intptr_t всегда должен заменять size_t и наоборот.
источник