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

477

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

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

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

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

  • 6
    Если это ваши единственные варианты, используйте int если some_size подписан, size_t если он не подписан.
  • 3
    @Nate Это неправильно. POSIX имеет тип ssize_t, но на самом деле правильный тип - ptrdiff_t.
Теги:
int
size-t

12 ответов

350
Лучший ответ

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

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

  • 4
    «Библиотечные функции, которые принимают или возвращают размеры, ожидают, что они будут иметь тип ... size_t» За исключением того, что stat () использует off_t для размера файла
  • 57
    @Draemon Этот комментарий отражает фундаментальную путаницу. size_t для объектов в памяти. Стандарт C даже не определяет stat() или off_t (это определения POSIX) или что-либо связанное с дисками или файловыми системами - он останавливается в потоках FILE . Управление виртуальной памятью полностью отличается от управления файловыми системами и файлами в том, что касается требований к размеру, поэтому упоминание off_t здесь неактуально.
Показать ещё 6 комментариев
173

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.

  • 6
    @Alok: два вопроса: 1) каков размер size_t ? 2) почему я должен предпочесть size_t чему-то вроде unsigned int ?
  • 1
    size_t не обязательно совпадает с unsigned int (кажется, вы подразумеваете, что они одинаковые).
Показать ещё 15 комментариев
59

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

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

unsigned char

unsigned short

unsigned int

unsigned long

unsigned long long

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

typedef unsigned long size_t;
  • 3
    Конечно, typedef unsigned long size_t зависит от компилятора. Или ты предлагаешь это всегда так?
  • 4
    @chux: Действительно, только потому, что одна реализация определяет это как таковое, не означает, что все делают. Показательный пример: 64-битная Windows. unsigned long - 32-битный, size_t - 64-битный.
Показать ещё 7 комментариев
46

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

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

  • 1
    У меня такая же среда, однако я протестировал ее на 32 бита, передав опцию "-m32" в GCC, результат был: "typedef unsigned int size_t". Спасибо, что поделились этой замечательной командой @Ciro, она мне очень помогла! :-)
  • 2
    Сам вопрос не сбивает с толку. Это сбивающий с толку ум пытается задать много вопросов и дать много ответов. Я удивлен, что этот ответ и ответ Арджуна Шридхарана до сих пор не мешают людям спрашивать и отвечать.
Показать ещё 1 комментарий
16

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

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

11

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

5

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

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

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

3

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

  • 2
    Я не могу принять вашу аргументацию. Вы говорите, что лучше, если ошибка переполнения молча приводит к доступу к действительным данным в вашем массиве?
  • 1
    @ maf-soft - это правильно. если ошибка остается незамеченной, это делает ее хуже, чем сбой программы. почему этот ответ получил отклик?
Показать ещё 1 комментарий
0

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
0

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

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

0

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

-5

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

Итак:

sizeof(size_t) >= sizeof(void*)
  • 15
    Не правда. Размер указателя может быть больше, чем size_t . Несколько примеров: компиляторы C в реальном режиме x86 могут иметь 32-битные указатели FAR или HUGE но size_t по-прежнему составляет 16 бит. Другой пример: у Watcom C был специальный толстый указатель для расширенной памяти, ширина которого составляла 48 бит, а size_t - нет. На встроенном контроллере с архитектурой Гарварда у вас также нет корреляции, потому что оба относятся к различным адресным пространствам.
  • 1
    И на этом stackoverflow.com/questions/1572099/… есть еще примеры AS / 400 с 128-битными указателями и 32-битным size_t
Показать ещё 1 комментарий

Ещё вопросы

Сообщество Overcoder
Наверх
Меню