без знака 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-документа .


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 (с очень хорошим примером)


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должен быть достаточно большим, чтобы представлять размер самого большого отдельного объекта.
— Кит Томпсон
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.