Меня путают с size_t
в C. Я знаю, что он возвращается оператором sizeof
. Но что именно это? Это тип данных?
Допустим, у меня есть цикл for
:
for(i = 0; i < some_size; i++)
Должен ли я использовать int i;
илиsize_t i;
?
Согласно стандарту 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
как этот файл внутренне sub включаетstddef.h
.Этот тип используется для представления размер объекта. Библиотечные функции что берут или возвращают размеры, ожидающие их быть типа или иметь тип возврата
size_t
. Далее самое часто используемый на основе компилятора Размер оператора должен оцениваться как постоянное значение, с которым совместимоsize_t
.
Как следствие, size_t
- это тип, гарантирующий наличие любого индекса массива.
size_t
- это неподписанный тип. Таким образом, он не может представлять никаких отрицательных значений (< 0). Вы используете его, когда считаете что-то, и уверены, что оно не может быть отрицательным. Например, 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
.
size_t
- это тип, который может содержать любой индекс массива.
В зависимости от реализации, это может быть любой из:
unsigned char
unsigned short
unsigned int
без подписи долго
"без подписи долго"
Вот как size_t
определяется в stddef.h
моей машины:
typedef unsigned long size_t;
Если вы эмпирический тип ,
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», я думаю, что он ограничивает максимальный размер любого массива.
См. Также: https://stackoverflow.com/questions/9386979/the-maximum-of-an-array-in-c
Поскольку никто еще не упомянул об этом, основная лингвистическая значимость size_t
заключается в том, что оператор sizeof
возвращает значение этого типа. Аналогично, основная значимость ptrdiff_t
заключается в том, что вычитание одного указателя из другого приведет к значению этого типа. Библиотечные функции, которые принимают это, делают это, потому что это позволит таким функциям работать с объектами, размер которых превышает UINT_MAX в системах, где такие объекты могут существовать, не заставляя абонентов тратить код, передавая значение больше &"без подписи int &" в системах, где больший тип будет достаточным для всех возможных объектов.
size_t
и int
не являются взаимозаменяемыми. Например, в 64-битном Linux size_t
имеет размер 64-битного (т.е. sizeof (void *)
), ноint
32-битный.
Также обратите внимание, что size_t
не подписан. Если вам нужна подписанная версия, на некоторых платформах есть ssize_t
, и это будет более актуально для вашего примера.
Как правило, я бы предложил использовать int
для большинства общих случаев и использовать size_t
/ ssize_t
, когда в этом есть конкретная потребность (например, с mmap ()
).
Чтобы понять, почему size_t
нужно было существовать и как мы сюда попали:
В прагматическом смысле size_t
и ptrdiff_t
гарантированно имеют ширину 64 бита в 64-битной реализации, ширину 32 бита в 32-битной реализации и так далее. Они не могли заставить любой существующий тип означать это на каждом компиляторе, не нарушая устаревший код.
size_t
или ptrdiff_t
не обязательно совпадает с intptr_t
или uintptr_t
. Они отличались по некоторым архитектурам, которые все еще использовались, когда size_t
и ptrdiff_t
были добавлены в Стандарт в конце 80-х, и устарели, когда C99 добавил много новых типов, но еще не исчез (например, 16-битную Windows). ). X86 в 16-битном защищенном режиме имел сегментированную память, где максимально возможный массив или структура могли иметь размер всего 65 536 байт, но указатель «далеко» должен был быть шириной 32 бита, шире, чем регистры. На них intptr_t
имел бы ширину 32 бита, но size_t
и ptrdiff_t
могли иметь ширину 16 бит и помещаться в регистр. И кто знал, какая операционная система может быть написана в будущем? Теоретически, архитектура i386 предлагает 32-битную модель сегментации с 48-битными указателями, которые фактически не использовались ни одной операционной системой.
Тип смещения памяти не может быть «длинным», потому что слишком много устаревшего кода предполагает, что «длинный» имеет ширину ровно 32 бита. Это предположение было даже встроено в API UNIX и Windows. К сожалению, многие другие устаревшие коды также предполагали, что «long» достаточно широк, чтобы удерживать указатель, смещение файла, количество секунд, прошедших с 1970 года, и так далее. POSIX теперь предоставляет стандартизированный способ заставить последнее предположение быть истинным, а не первым, но ни одно из них не является переносимым предположением.
Это не могло быть "int", потому что только крошечная горстка компиляторов в 90-х годах сделала "int" 64 бита в ширину. Тогда они действительно стали странными, сохраняя «длинные» 32 бита в ширину. Следующая редакция Стандарта объявила незаконным, чтобы «int» был шире, чем «long», но «int» по-прежнему имеет ширину 32 бита в большинстве 64-битных систем.
Это не может быть «длинно-длинный инт», который в любом случае был добавлен позже, поскольку он был создан, чтобы иметь ширину не менее 64 бит даже в 32-битных системах.
Итак, новый тип был необходим. Даже если это не так, все эти другие типы означали что-то иное, чем смещение в массиве или объекте. И если был один урок из фиаско 32-64-битной миграции, он должен был быть конкретным относительно того, какие свойства должен иметь тип, а не использовать тот, который означал разные вещи в разных программах.
В общем, если вы начинаете с 0 и идете вверх, всегда используйте неподписанный тип, чтобы избежать переполнения, приводящего вас в ситуацию с отрицательным значением. Это критически важно, потому что, если границы вашего массива оказываются меньше максимума вашего цикла, но максимальный цикл превышает максимальный уровень вашего типа, вы оборачиваете отрицательный и можете испытать [ошибку сегментации] [ 1] (SIGSEGV). Так что, как правило, никогда не используйте int для цикла, начинающегося с 0 и идущего вверх. Используйте неподписанный.
size_t - это целочисленный тип данных без знака. В системах, использующих библиотеку GNU C, это будет & # 160; unsigned & # 160; int & # 160; or & # 160; unsigned & # 160; int. size_t & # 160; обычно используется для индексации массива и подсчета циклов.
size_t или любой неподписанный тип может рассматриваться как переменная цикла, поскольку переменные цикла обычно больше или равны 0.
Когда мы используем объект size_t , мы должны убедиться, что во всех контекстах, которые он использует, включая арифметику, мы хотим только неотрицательные значения. Например, следующая программа определенно даст неожиданный результат:
// C program to demonstrate that size_t or
// any unsigned int type should be used
// carefully when used in a loop
#include<stdio.h>
int main()
{
const size_t N = 10;
int a[N];
// This is fine
for (size_t n = 0; n < N; ++n)
a[n] = n;
// But reverse cycles are tricky for unsigned
// types as can lead to infinite loop
for (size_t n = N-1; n >= 0; --n)
printf("%d ", a[n]);
}
Output
Infinite loop and then segmentation fault
Насколько я понимаю, size_t
- это целое число unsigned
, размер бита которого достаточно велик, чтобы удерживать указатель на нативную архитектуру.
Так:
sizeof(size_t) >= sizeof(void*)