Что такое size_t в C? - PullRequest
       33

Что такое size_t в C?

556 голосов
/ 31 марта 2010

Я путаюсь с size_t в C. Я знаю, что он возвращается оператором sizeof. Но что именно это? Это тип данных?

Допустим, у меня есть for цикл:

for(i = 0; i < some_size; i++)

Должен ли я использовать int i; или size_t i;?

Ответы [ 12 ]

420 голосов
/ 31 марта 2010

Из Википедии :

Согласно стандарту ISO C 1999 года (C99), size_t - целое число без знака тип не менее 16 бит (см. разделы 7.17 и 7.18.3).

size_t - тип данных без знака определяется несколькими стандартами C / C ++, например стандарт C99 ISO / IEC 9899, что определено в stddef.h. 1 Может в дальнейшем импортировать путем включения stdlib.h как этот файл внутренне включает stddef.h.

Этот тип используется для представления размер объекта. Библиотечные функции что взять или вернуть размеры ожидают их быть типом или иметь тип возврата size_t. Далее наиболее часто используемые компиляторы оператор sizeof должен оценить постоянное значение, которое совместимо с size_t.

Как следствие, size_t - это тип, гарантированно содержащий любой индекс массива.

203 голосов
/ 31 марта 2010

size_t - беззнаковый тип. Таким образом, он не может представлять отрицательные значения (<0). Вы используете это, когда вы рассчитываете что-то, и уверены, что это не может быть отрицательным. Например, <a href="http://devdocs.io/c/string/byte/strlen" rel="noreferrer">strlen() возвращает size_t, поскольку длина строки должна быть не менее 0.

В вашем примере, если ваш индекс цикла будет всегда больше 0, возможно, имеет смысл использовать size_t или любой другой тип данных без знака.

Когда вы используете объект size_t, вы должны убедиться, что во всех контекстах, в которых он используется, включая арифметику, вам нужны неотрицательные значения. Например, допустим, у вас есть:

size_t s1 = strlen(str1);
size_t s2 = strlen(str2);

и вы хотите найти разницу длин str2 и str1. Вы не можете сделать:

int diff = s2 - s1; /* bad */

Это потому, что значение, присвоенное diff, всегда будет положительным числом, даже когда s2 < s1, потому что вычисление выполняется с беззнаковыми типами. В этом случае, в зависимости от вашего варианта использования, вам может быть лучше использовать int (или long long) для s1 и s2.

В C / POSIX есть некоторые функции, которые могут / должны использовать size_t, но не по историческим причинам. Например, вторым параметром fgets в идеале должно быть size_t, но это int.

67 голосов
/ 25 февраля 2014

size_t - это тип, который может содержать любой индекс массива.

В зависимости от реализации может быть любой из:

unsigned char

unsigned short

unsigned int

unsigned long

unsigned long long

Вот как size_t определяется в stddef.h моей машины:

typedef unsigned long size_t;
63 голосов

Если вы эмпирический тип ,

echo | gcc -E -xc -include 'stddef.h' - | grep size_t

Выход для Ubuntu 14.04 64-битный GCC 4.8:

typedef long unsigned int size_t;

Обратите внимание, что stddef.hпредоставляется GCC, а не glibc по src/gcc/ginclude/stddef.h в GCC 4.2.

Интересные появления C99

  • malloc принимает size_t в качествеаргумент, поэтому он определяет максимальный размер, который может быть выделен.

    И так как он также возвращается sizeof, я думаю, что он ограничивает максимальный размер любого массива.

    См. также: Какой максимальный размер массива в C?

21 голосов
/ 31 марта 2010

Справочная страница для types.h говорит:

size_t должен быть целым типом без знака

15 голосов
/ 05 октября 2016

Поскольку никто еще не упомянул об этом, основное лингвистическое значение size_t состоит в том, что оператор sizeof возвращает значение этого типа. Аналогично, основное значение ptrdiff_t состоит в том, что вычитание одного указателя из другого приведет к значению этого типа. Библиотечные функции, которые принимают его, делают это, потому что это позволит таким функциям работать с объектами, размер которых превышает UINT_MAX в системах, где такие объекты могут существовать, не вынуждая вызывающих абонентов тратить код, передавая значение, большее, чем «unsigned int», в системах, где больший тип будет достаточно для всех возможных объектов.

7 голосов
/ 30 октября 2013

size_t и int не являются взаимозаменяемыми. Например, в 64-битном Linux size_t имеет 64-битный размер (т.е. sizeof(void*)), но int - 32-битный.

Также обратите внимание, что size_t не подписано. Если вам нужна подписанная версия, то на некоторых платформах есть ssize_t, и она будет более подходящей для вашего примера.

Как общее правило, я бы предложил использовать int для большинства общих случаев и использовать size_t / ssize_t только в случае особой необходимости (например, mmap()).

4 голосов
/ 17 марта 2019

Чтобы понять, почему size_t нужно существовать и как мы сюда попали:

В практическом плане size_t и ptrdiff_t гарантированно будут иметь ширину 64 бита в 64-битной реализации, ширину 32 бита в 32-битной реализации и так далее. Они не могли заставить любой существующий тип означать это на каждом компиляторе, не нарушая устаревший код.

A size_t или ptrdiff_t не обязательно совпадает с intptr_t или uintptr_t. Они отличались в некоторых архитектурах, которые все еще использовались, когда size_t и ptrdiff_t были добавлены в стандарт в конце 80-х, и устарели, когда C99 добавил много новых типов, но еще не ушел (например, 16-битная Windows ). Сервер x86 в 16-разрядном защищенном режиме имел сегментированную память, в которой максимальный размер массива или структуры мог составлять всего 65 536 байт, но указатель far должен был иметь ширину 32 бита, шире регистров. Для них intptr_t имел бы ширину 32 бита, но size_t и ptrdiff_t могли бы иметь ширину 16 бит и помещаться в регистр. И кто знал, какая операционная система может быть написана в будущем? Теоретически, архитектура i386 предлагает 32-битную модель сегментации с 48-битными указателями, которую никогда не использовала ни одна операционная система.

Тип смещения памяти не может быть long, поскольку слишком много устаревшего кода предполагает, что long имеет ширину ровно 32 бита. Это предположение было даже встроено в API-интерфейсы UNIX и Windows. К сожалению, многие другие устаревшие коды также предполагали, что long достаточно широк, чтобы содержать указатель, смещение файла, количество секунд, прошедших с 1970 года, и так далее. POSIX теперь предоставляет стандартизированный способ заставить последнее предположение быть верным вместо первого, но ни одно из них не является переносимым.

Это не может быть int, потому что только крошечная горстка компиляторов в 90-х годах имела ширину int 64 бит. Тогда они действительно стали странными, держа ширину long 32 бита. В следующей редакции стандарта было объявлено, что int шире, чем long, незаконно, но в большинстве 64-битных систем int по-прежнему имеет ширину 32 бита.

Это не может быть long long int, который в любом случае был добавлен позже, поскольку он был создан, чтобы иметь ширину не менее 64 бит даже в 32-битных системах.

Итак, нужен новый тип. Даже если это не так, все эти другие типы означают нечто иное, чем смещение в массиве или объекте. И если бы был один урок из фиаско 32-битной миграции, то нужно было конкретно указать, какие свойства должен иметь тип, и не использовать тот, который имел разные значения в разных программах.

3 голосов
/ 09 ноября 2013

В общем, если вы начинаете с 0 и идете вверх, всегда используйте тип без знака, чтобы избежать переполнения, приводящего к ситуации с отрицательным значением. Это критически важно, потому что, если границы вашего массива оказываются меньше, чем максимум вашего цикла, но максимум вашего цикла оказывается больше, чем максимум вашего типа, вы обернетесь вокруг отрицательного значения и у вас может возникнуть сегментация ошибка (SIGSEGV). Поэтому, вообще, никогда не используйте int для цикла, начинающегося с 0 и идущего вверх. Используйте без знака.

2 голосов
/ 12 августа 2017

size_t - целочисленный тип данных без знака. В системах, использующих библиотеку GNU C, это будет unsigned int или unsigned long int. size_t обычно используется для индексации массива и подсчета циклов.

...