Я запутался с size_t
в C. Я знаю, что он возвращается оператором sizeof
. Но что это такое? Это тип данных?
Скажем, у меня есть цикл for
:
for(i = 0; i < some_size; i++)
Должен ли я использовать int i;
или size_t i;
?
Согласно стандарту ISO ISO 1999 (C99),
size_t
- целое число без знака тип не менее 16 бит (см. разделы 7.17 и 7.18.3).
size_t
- неподписанный тип данных определенных несколькими стандартами C/С++, например стандарт C99 ISO/IEC 9899, который определяется вstddef.h
. 1 Он может быть дополнительно импортированы путем включенияstdlib.h
, так как этот внутренний файл включаетstddef.h
.Этот тип используется для представления размер объекта. Функции библиотеки которые берут или возвращают размеры, ожидают их быть типом или иметь тип возврата
size_t
. Кроме того, наиболее часто используемые компиляторы оператор sizeof должен оценивать постоянное значение, совместимое сsize_t
.
В качестве импликации size_t
является типом, гарантирующим, что будет содержать любой индекс массива.
size_t
для объектов в памяти. Стандарт C даже не определяет stat()
или off_t
(это определения POSIX) или что-либо связанное с дисками или файловыми системами - он останавливается в потоках FILE
. Управление виртуальной памятью полностью отличается от управления файловыми системами и файлами в том, что касается требований к размеру, поэтому упоминание off_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
? 2) почему я должен предпочесть size_t
чему-то вроде unsigned int
?
size_t
не обязательно совпадает с unsigned int
(кажется, вы подразумеваете, что они одинаковые).
size_t
- это тип, который может содержать любой индекс массива.
В зависимости от реализации это может быть любое из:
unsigned char
unsigned short
unsigned int
unsigned long
unsigned long long
Здесь size_t
определяется в stddef.h
моей машины:
typedef unsigned long size_t;
typedef unsigned long size_t
зависит от компилятора. Или ты предлагаешь это всегда так?
unsigned long
- 32-битный, size_t
- 64-битный.
Если вы эмпирический тип
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
В manpage для types.h говорится:
size_t должен быть целым числом без знака
Поскольку никто еще не упомянул об этом, основным лингвистическим значением size_t
является то, что оператор sizeof
возвращает значение этого типа. Аналогично, основным значением ptrdiff_t
является то, что вычитание одного указателя из другого даст значение этого типа. Функции библиотеки, которые его принимают, делают это, потому что это позволит таким функциям работать с объектами, размер которых превышает UINT_MAX в системах, где такие объекты могут существовать, не заставляя вызывающих абонентов тратить код, передавая значение, большее, чем "unsigned int" в системах, где больший тип будет достаточным для всех возможных объектов.
size_t
и int
не являются взаимозаменяемыми. Например, в 64-разрядной версии Linux size_t
имеется 64-разрядный размер (т.е. sizeof(void*)
), но int
- 32-разрядный.
Также обратите внимание, что size_t
не имеет знака. Если вам нужна подписанная версия, на некоторых платформах есть ssize_t
, и это будет более актуально для вашего примера.
Как правило, я бы предложил использовать int
для большинства общих случаев и использовать только size_t
/ssize_t
, когда есть определенная потребность в нем (например, mmap()
).
В общем, если вы начинаете с 0 и поднимаетесь вверх, всегда используйте неподписанный тип, чтобы избежать переполнения, что привело вас к ситуации с отрицательным значением. Это критически важно, потому что, если границы вашего массива будут меньше, чем максимальный ваш цикл, но ваш цикл max окажется больше максимального вашего типа, вы обернете негатив, и вы можете столкнуться с ошибка сегментации (SIGSEGV). Поэтому, вообще говоря, никогда не используйте int для цикла, начинающегося с 0 и идущего вверх. Используйте unsigned.
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 - это тип, возвращаемый оператором sizeof, и широко используется в стандартной библиотеке для представления размеров и подсчетов.
size_t - целочисленный тип данных без знака. В системах, использующих библиотеку GNU C, это будет unsigned int или unsigned long int. size_t обычно используется для индексирования массива и подсчета циклов.
С моей точки зрения, size_t
- это целое число unsigned
, размер бит которого достаточно велик, чтобы удерживать указатель собственной архитектуры.
Итак:
sizeof(size_t) >= sizeof(void*)
size_t
. Несколько примеров: компиляторы C в реальном режиме x86 могут иметь 32-битные указатели FAR
или HUGE
но size_t по-прежнему составляет 16 бит. Другой пример: у Watcom C был специальный толстый указатель для расширенной памяти, ширина которого составляла 48 бит, а size_t
- нет. На встроенном контроллере с архитектурой Гарварда у вас также нет корреляции, потому что оба относятся к различным адресным пространствам.
size_t
int
еслиsome_size
подписан,size_t
если он не подписан.