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

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

Скажем, у меня есть цикл for:

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

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

Ответ 1

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

Согласно стандарту 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 является типом, гарантирующим, что будет содержать любой индекс массива.

Ответ 2

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.

Ответ 3

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

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

unsigned char

unsigned short

unsigned int

unsigned long

unsigned long long

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

typedef unsigned long size_t;

Ответ 4

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

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

Выход для Ubuntu 14.04 64-bit 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?

Ответ 5

В manpage для types.h говорится:

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

Ответ 6

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

Ответ 7

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

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

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

Ответ 8

Чтобы понять, почему 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 байт, но 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, но int по-прежнему имеет ширину 32 бита в большинстве 64-битных систем.

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

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

Ответ 9

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

Ответ 10

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

Ответ 11

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

Ответ 12

Псевдоним одного из фундаментальных целочисленных типов без знака.

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

Ответ 13

С моей точки зрения, size_t - это целое число unsigned, размер бит которого достаточно велик, чтобы удерживать указатель собственной архитектуры.

Итак:

sizeof(size_t) >= sizeof(void*)