без знака int против size_t

493

Я заметил, что современный код на C и C ++, кажется, использует size_tвместо int/ unsigned intпочти везде - от параметров для строковых функций C до STL. Мне любопытно узнать причину этого и преимущества, которые оно приносит.

обкрадывать
источник

Ответы:

388

size_tТип является целым числом без знака тип , который является результатом sizeofоператора (и offsetofоператора), поэтому он гарантированно будет достаточно большим , чтобы содержать размер самого большого объекта система может обрабатывать (например, статический массив 8Gb).

size_tТипа может быть больше, равно или меньше , чем unsigned int, и компилятор может делать предположения об этом для оптимизации.

Вы можете найти более точную информацию в стандарте C99, раздел 7.17, черновик которого доступен в Интернете в формате pdf , или в стандарте C11, раздел 7.19, который также доступен в виде PDF-документа .

Remo.D
источник
50
Нет. Подумайте о x86-16 с большой (не огромной) моделью памяти: указатели далеко (32-битные), но отдельные объекты ограничены 64 КБ (поэтому size_t может быть 16-битным).
Ден04
8
«Размер самого большого объекта» не плохая формулировка, но абсолютно правильная. Шесть объектов может быть гораздо более ограниченной, чем адресное пространство.
gnasher729
3
«Ваш компилятор может сделать предположение»: я надеюсь, что компилятор знает точный диапазон значений, которые size_tмогут представлять! Если нет, то кто?
Марк ван Леувен
4
@Marc: Я думаю, что дело было в том, что компилятор мог бы что-то сделать с этими знаниями.
8
Я просто хотел бы, чтобы этот все более популярный тип не требовал включения заголовочного файла.
user2023370 11.11.16
98

Классический C (ранний диалект C, описанный Брайаном Керниганом и Деннисом Ритчи в The C Programming Language, Prentice-Hall, 1978) не предоставил size_t. Комитет по стандартам C введен size_tдля устранения проблемы переносимости

Подробно объяснено на Embedded.com (с очень хорошим примером)

azeemarif
источник
6
Еще одна замечательная статья, объясняющая как size_t, так и ptrdiff_t: viva64.com/en/a/0050
Игорь Кахарличенко
74

Короче говоря, size_tникогда не бывает отрицательным, и это максимизирует производительность, потому что это typedef'd, чтобы быть целым типом без знака, который достаточно большой - но не слишком большой - чтобы представить размер максимально возможного объекта на целевой платформе.

Размеры никогда не должны быть отрицательными, и это действительно size_tтип без знака. Кроме того, поскольку size_tбез знака, вы можете хранить числа, которые примерно в два раза больше, чем у соответствующего типа со знаком, потому что мы можем использовать бит знака для представления величины, как и все другие биты в целом числе без знака. Когда мы получаем еще один бит, мы умножаем диапазон чисел, которые мы можем представить, примерно в два раза.

Итак, вы спросите, почему бы просто не использовать unsigned int? Возможно, он не сможет вместить достаточно большие числа. В реализации, где unsigned int32 бита, наибольшее число, которое он может представлять, - 4294967295. Некоторые процессоры, такие как IP16L32, могут копировать объекты размером более 4294967295байтов.

Итак, вы спросите, почему бы не использовать unsigned long int? Это требует снижения производительности на некоторых платформах. Стандарт C требует, чтобы он longзанимал не менее 32 бит. Платформа IP16L32 реализует каждый 32-разрядный код в виде пары 16-разрядных слов. Почти всем 32-битным операторам на этих платформах требуются две инструкции, если не больше, потому что они работают с 32-битными в двух 16-битных блоках. Например, для перемещения 32-битной длины обычно требуются две машинные инструкции - по одной для перемещения каждой 16-битной порции.

Использование size_tпозволяет избежать потери производительности. Согласно этой фантастической статье , «Тип size_t- это typedef, который является псевдонимом для некоторого целого типа без знака, как правило, unsigned intили unsigned long, возможно, даже unsigned long long. Предполагается, что каждая реализация Standard C выбирает целое число без знака, которое достаточно велико - но не больше, чем нужно - представлять размер максимально возможного объекта на целевой платформе. "

Роза Перроне
источник
1
Извините, что прокомментировал это так долго, но мне просто нужно было подтвердить самое большое число, которое может содержать неподписанный int - возможно, я неправильно понимаю вашу терминологию, но я подумал, что самое большое число, которое может содержать unsigned int, это 4294967295, 65356 максимум неподписанного короткого.
Митч
Если ваш unsigned int занимает 32 бита, то да, самое большое число, которое он может содержать, это 2 ^ 32 - 1, что составляет 4294967295 (0xffffffff). У вас есть еще один вопрос?
Роуз Перроне
3
@Mitch: наибольшее значение, которое может быть представлено в банке, unsigned intи оно варьируется от одной системы к другой. Это должно быть как минимум 65536 , но обычно 4294967295и может быть 18446744073709551615(2 ** 64-1) в некоторых системах.
Кит Томпсон
1
Наибольшее значение, которое может содержать 16-битное целое без знака, равно 65535, а не 65536. Небольшое, но важное отличие, поскольку 65536 такое же, как 0 в 16-битном целом без знака.
Sie Raybould
1
@ gnasher729: Вы уверены в стандарте C ++? Поискав некоторое время, у меня сложилось впечатление, что они просто сняли все абсолютные гарантии относительно целочисленных диапазонов (исключая unsigned char). Стандарт, кажется, нигде не содержит строку «65535» или «65536», а «+32767» встречается только (1.9: 9) в примечании как возможное наибольшее целое число, представимое в int; гарантия не дается даже, что INT_MAXне может быть меньше, чем это!
Марк ван Леувен
51

Тип size_t - это тип, возвращаемый оператором sizeof. Это целое число без знака, способное выражать размер в байтах любого диапазона памяти, поддерживаемого на хост-машине. Он (как правило) связан с ptrdiff_t тем, что ptrdiff_t является целочисленным значением со знаком, так что sizeof (ptrdiff_t) и sizeof (size_t) равны.

При написании кода на C вы всегда должны использовать size_t при работе с диапазонами памяти.

Тип int, с другой стороны, в основном определяется как размер целого значения (со знаком), которое хост-машина может использовать для наиболее эффективного выполнения целочисленной арифметики. Например, на многих старых компьютерах типа ПК значение sizeof (size_t) будет равно 4 (байты), а sizeof (int) будет равно 2 (байт). 16-битная арифметика была быстрее, чем 32-битная арифметика, хотя процессор мог обрабатывать (логическое) пространство памяти до 4 ГиБ.

Используйте тип int только тогда, когда вы заботитесь об эффективности, поскольку его фактическая точность сильно зависит как от параметров компилятора, так и от архитектуры машины. В частности, стандарт C определяет следующие инварианты: sizeof (char) <= sizeof (short) <= sizeof (int) <= sizeof (long) и не устанавливает других ограничений на фактическое представление точности, доступной программисту для каждого из эти примитивные типы.

Примечание: это НЕ то же самое, что в Java (которая фактически определяет битовую точность для каждого из типов 'char', 'byte', 'short', 'int' и 'long').

Кевин С.
источник
де-факто определение int состоит в том, что он 16-битный на 16 машинах и 32-битный на чем-то большем. Было написано слишком много кода, который предполагает, что int имеет ширину 32 бита, чтобы изменить это сейчас, и в результате люди всегда должны использовать size_t или {, u} int {8,16,32,64} _t, если они хотят что-то конкретное - - в качестве меры предосторожности люди должны всегда использовать их вместо целочисленных целочисленных типов.
Четче
3
«Это целое число без знака, способное выражать размер в байтах любого диапазона памяти, поддерживаемого на хост-машине». -> Нет. size_tСпособен отображать размер любого отдельного объекта (например: число, массив, структура). Весь диапазон памяти может превышатьsize_t
chux - Восстановить Монику
«При написании кода на C вы всегда должны использовать size_t при работе с диапазонами памяти». - это означает, что каждый индекс для каждого массива должен быть size_t- я надеюсь, вы не имеете в виду это. Большую часть времени мы не имеем дело с массивами, где кардинальность адресного пространства + переносимость имеют значение. В этих случаях вы бы взяли size_t. В любом другом случае вы берете индексы из (подписанных) целых чисел. Потому что путаница (которая приходит без предупреждения), возникающая из-за непредвиденного недопустимого поведения неподписанных, встречается чаще и хуже проблем переносимости, которые могут возникнуть в других случаях.
johannes_lalala
23

Тип size_t должен быть достаточно большим, чтобы хранить размер любого возможного объекта. Целое число без знака не должно удовлетворять этому условию.

Например, в 64-битных системах int и unsigned int могут иметь ширину 32 бита, но size_t должен быть достаточно большим, чтобы хранить числа больше 4G.

Мацей Хель
источник
38
«объект» - это язык, используемый стандартом.
R .. GitHub ОСТАНОВИТЬ ЛЬДА
2
Я думаю, size_tчто он должен был бы быть настолько большим, если бы компилятор мог принять тип X такой, чтобы sizeof (X) давал значение больше 4G. Например typedef unsigned char foo[1000000000000LL][1000000000000LL], большинство компиляторов будут отклонены , и даже foo[65536][65536];могут быть законно отклонены, если он превысит задокументированный предел реализации.
суперкат
1
@MattJoiner: формулировка в порядке. «Объект» вовсе не расплывчат, а скорее означает «область хранения».
Гонки легкости на орбите
4

Этот отрывок из руководства glibc 0.02 также может быть актуален при исследовании темы:

Существует потенциальная проблема с типом size_t и версиями GCC до выпуска 2.4. ANSI C требует, чтобы size_t всегда был беззнаковым типом. Для совместимости с заголовочными файлами существующих систем, GCC определяет size_t в stddef.h' to be whatever type the system'ssys / types.h и определяет его как. Большинство систем Unix, которые определяют size_t в `sys / types.h ', определяют его как тип со знаком. Некоторый код в библиотеке зависит от size_t, являющегося типом без знака, и не будет работать правильно, если он подписан.

Код библиотеки GNU C, который ожидает, что size_t будет без знака, является правильным. Определение size_t как подписанного типа неверно. Мы планируем, что в версии 2.4 GCC всегда будет определять size_t как тип без знака и fixincludes' script will massage the system'ssys / types.h ', чтобы не конфликтовать с этим.

Тем временем, мы обходим эту проблему, явно говоря GCC, чтобы при компиляции библиотеки GNU C использовался тип unsigned для size_t. `configure 'автоматически определит, какой тип GCC использует для size_t, чтобы переопределить его при необходимости.

Грэм Берк
источник
3

Если мой компилятор установлен на 32 бит, size_tэто не что иное, как typedef для unsigned int. Если мой компилятор установлен на 64 бит, size_tэто не что иное, как typedef для unsigned long long.

рерио
источник
1
Может быть определено как unsigned longдля обоих случаев на некоторых ОС.
StaceyGirl
-4

size_t - размер указателя.

Таким образом, в 32-битной или общей ILP32 (целое, длинное, указатель) модель size_t составляет 32 бита. и в 64-битной или обычной модели LP64 (long, pointer) size_t равен 64 битам (целые числа по-прежнему 32 бит).

Существуют и другие модели, но именно они используют g ++ (по крайней мере, по умолчанию).


источник
15
size_tне обязательно такой же размер, как указатель, хотя обычно это так. Указатель должен указывать на любое место в памяти; size_tдолжен быть достаточно большим, чтобы представлять размер самого большого отдельного объекта.
Кит Томпсон