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

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

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

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

Что мне использовать: int i; или size_t i;?


person Vijay    schedule 31.03.2010    source источник
comment
Если это ваши единственные варианты, используйте int, если some_size подписан, size_t, если он не подписан.   -  person Nate    schedule 31.03.2010
comment
@Nate Это неверно. POSIX имеет тип ssize_t, но на самом деле правильный тип для использования - ptrdiff_t.   -  person Molossus Spondee    schedule 19.03.2017
comment
Ответы не столь однозначны, как в Низкоуровневое программирование: C, сборка и выполнение программ на Intel® 64. Как сказано в книге, использования индекса int i может быть недостаточно для адресации огромного массива. Таким образом, используя size_t i, вы можете обращаться к большему количеству индексов, поэтому даже если у вас огромный массив, это не должно быть проблемой. size_t - это тип данных: обычно unsigned long int, но это зависит от вашей системы.   -  person bruno    schedule 14.02.2020


Ответы (14)


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

Согласно стандарту ISO C (C99) 1999 г., 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 - это тип, который гарантированно содержит любой индекс массива.

person sblom    schedule 31.03.2010
comment
Библиотечные функции, которые принимают или возвращают размеры, ожидают, что они будут иметь тип ... size_t За исключением того, что stat () использует off_t для размера файла - person Draemon; 27.05.2010
comment
@Draemon Этот комментарий отражает фундаментальную путаницу. size_t предназначен для объектов в памяти. Стандарт C даже не определяет stat() или off_t (это определения POSIX) или чего-либо, связанного с дисками или файловыми системами - он останавливается на FILE потоках. Управление виртуальной памятью полностью отличается от файловых систем и управления файлами в том, что касается требований к размеру, поэтому упоминание off_t здесь неуместно. - person jw013; 10.06.2013
comment
@ jw013: Я бы не назвал это фундаментальной неразберихой, но вы заметили интересный момент. Тем не менее, в цитируемом тексте не указаны размеры объектов в памяти, и смещение вряд ли можно назвать подходящим названием для типа размера, независимо от того, где он хранится. - person Draemon; 14.06.2013
comment
@Draemon Хороший вопрос. Этот ответ цитирует Википедию, которая, на мой взгляд, не имеет лучшего объяснения. Сам стандарт C гораздо более ясен: он определяет size_t как тип результата оператора sizeof (7.17p2 о <stddef.h>). Раздел 6.5 объясняет, как именно работают выражения C (6.5.3.4 для sizeof). Поскольку вы не можете применить sizeof к файлу на диске (в основном потому, что C даже не определяет, как работают диски и файлы), здесь нет места для путаницы. Другими словами, вините Википедию (и этот ответ за цитирование Википедии, а не фактического стандарта C). - person jw013; 14.06.2013
comment
@Draemon - Я также согласен с оценкой фундаментальной путаницы. Если вы не читали стандарты C / C ++, вы можете подумать, что объект относится к объектно-ориентированному программированию, но это не так. Прочтите стандарт C, в котором нет ни одного из этих объектов ООП, но все же есть объекты, и узнайте. Ответ может вас удивить! - person Heath Hunnicutt; 29.10.2013
comment
Также стоит отметить, что size_t (почти наверняка) является typedef для некоторого существующего целочисленного типа без знака. Например, size_t обычно того же типа, что и unsigned long (typedef создает псевдоним для существующего типа, а не для нового типа). Но вы не должны предполагать, что size_t - это то же самое, что и любой конкретный тип, поскольку он может варьироваться от системы к системе. - person Keith Thompson; 06.08.2015
comment
так 16 бит означает, что это просто короткое замыкание без знака? - person Ungeheuer; 31.03.2017
comment
Ну, sizeof говорит мне, что это 8 байт, поэтому определенно больше 16 бит, по крайней мере, для меня. - person Ungeheuer; 31.03.2017
comment
Не могли бы вы обновить ответ, чтобы ответить на вторую часть вопроса: «Следует ли мне использовать int i; или size_t i ;? ' - person Manuel Selva; 17.09.2019

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.

person Alok Singhal    schedule 31.03.2010
comment
@Alok: Два вопроса: 1) каков размер size_t? 2) почему я должен предпочесть size_t чему-то вроде unsigned int? - person Lazer; 08.06.2010
comment
size_t не обязательно будет тем же, что и unsigned int (вы, кажется, имеете в виду, что они такие же). - person Brendan Long; 08.06.2010
comment
@Lazer: размер size_t равен sizeof(size_t). Стандарт C гарантирует, что SIZE_MAX будет не менее 65535. size_t - это тип, возвращаемый оператором sizeof, и используется в стандартной библиотеке (например, strlen возвращает size_t). Как сказал Брендан, size_t не обязательно должно совпадать с unsigned int. - person Alok Singhal; 09.06.2010
comment
@Alok, @Brendan Long: Когда вы говорите, что size_t - это не то же самое, что unsigned int, я думаю, вы имеете в виду, что не гарантируется, что это будет int, хотя на самом деле это unsigned. Это правда? - person Lazer; 13.06.2010
comment
@Lazer - да, size_t гарантированно беззнаковый тип. - person Alok Singhal; 13.06.2010
comment
s2 - s1 не всегда будет положительным числом. - person jsimmons; 13.06.2011
comment
@jsimmons: это была моя точка зрения. Если s1 и s2 имеют беззнаковый тип (например, size_t), результат s1-s2 не может быть отрицательным, даже если s1 < s2. Это потому, что беззнаковые типы не имеют отрицательных значений. Когда вы присваиваете эту разницу int, результат определяется реализацией. В этом и заключалась вся суть в том, чтобы сказать, что никто не хочет принимать во внимание различие беззнаковых типов в моем примере, потому что нужно иметь возможность переносимо получить возможную отрицательную разницу. - person Alok Singhal; 14.06.2011
comment
Итак, он может представлять неотрицательные значения, не так ли? - person Celeritas; 28.09.2013
comment
@Celeritas нет, я имею в виду, что беззнаковый тип может представлять только неотрицательные значения. Я, наверное, должен был сказать, что он не может представлять отрицательные значения. - person Alok Singhal; 28.09.2013
comment
Это потому, что s2 - s1 НЕ всегда будет положительным числом (например, s2 = 5; s1 = 20; s2-s1 = -15). - person Derek Johnson; 09.10.2013
comment
@DerekJohnson Я разъяснил эту часть своего ответа. - person Alok Singhal; 09.10.2013
comment
Имейте в виду, что в 64-битном Linux int всегда 32-битный, а size_t - 64-битный. Таким образом, size_t и int НЕ взаимозаменяемы. - person dtoux; 30.10.2013
comment
Если по каким-либо причинам вам нужен размер для подписи, в Linux есть тип ssize_t. - person dtoux; 30.10.2013
comment
Это не то, как работает два дополнения! size_t s2 = 5, s1 = 20; int diff = s2 - s1; printf("%d: %x\n", diff, diff < 0); Это напечатает именно то, что вы ожидаете: "-15: 1" - person Jason Oster; 02.07.2015
comment
@JasonOster, дополнение до двух не является требованием стандарта C. Если значение s2 - s1 превышает int, поведение не определено. - person Alok Singhal; 06.07.2015
comment
@AlokSinghal Если говорить о спецификации C педантично, то вы правы. Реальность такова, что дополнение ограничено небольшим количеством исторических мэйнфреймов и несколькими специальными приложениями, такими как АЦП. Фактически, дополнение до двух настолько распространено, что к настоящему времени стало стандартом de facto. Целочисленные переполнения могут быть опасными, но их применение выходит за рамки смешивания знаков. - person Jason Oster; 06.07.2015

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

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

unsigned char

unsigned short

unsigned int

unsigned long

unsigned long long

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

typedef unsigned long size_t;
person Arjun Sreedharan    schedule 25.02.2014
comment
Конечно, typedef unsigned long size_t зависит от компилятора. Или вы предполагаете, что это всегда так? - person chux - Reinstate Monica; 31.10.2014
comment
@chux: Действительно, то, что одна реализация определяет это как таковое, не означает, что все это делают. Показательный пример: 64-битная Windows. unsigned long - 32-битный, size_t - 64-битный. - person Tim Čas; 29.12.2014
comment
в чем именно предназначение size_t? Когда я могу создать для себя переменную, например: int mysize_t; или длинный mysize_t или беззнаковый длинный mysize_t. Почему кто-то должен был создать для меня эту переменную? - person midkin; 31.07.2016
comment
@midkin size_t не является переменной. Это тип, который вы можете использовать, когда хотите представить размер объекта в памяти. - person Arjun Sreedharan; 01.08.2016
comment
правда ли, что size_t всегда 32 бита на 32-битной машине, 64 бита тоже? - person John Wu; 24.08.2016
comment
Согласно стандарту ISO C (C99) 1999 г., size_t представляет собой целочисленный тип без знака длиной не менее 16 бит (см. Разделы 7.17 и 7.18.3). Так это не может быть unsigned char? - person jameshfisher; 30.11.2016
comment
@jameshfisher Я не уверен, что ограничение 16 бит верно. uint_least16_t - это как минимум 16 бит. О, size_t, стандарт говорит беззнаковый целочисленный тип результата оператора sizeof, а оператор sizeof дает размер (в байтах) его операнда. - person Arjun Sreedharan; 30.11.2016
comment
@jameshfisher, который говорит, что unsigned char не может быть 16-битным ?! - person Antti Haapala; 16.10.2017
comment
@AnttiHaapala, это хороший аргумент; очевидно, гарантия состоит в том, что unsigned char содержит хотя бы значения 0-255. - person jameshfisher; 16.10.2017

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

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

Вывод для 64-разрядной версии Ubuntu 14.04 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?

person Ciro Santilli 新疆再教育营六四事件ۍ    schedule 14.06.2015
comment
У меня такая же среда, однако я тестировал ее для 32 бит, передав параметр GCC -m32, результат был: typedef unsigned int size_t. Спасибо, что поделились этой замечательной командой @Ciro, она мне очень помогла! :-) - person silvioprog; 22.03.2017
comment
Само по себе дело не сбивает с толку. Это сбивающий с толку ум, который пытается задать много вопросов и дать много ответов. Я удивлен, что этот ответ и ответ Арджуна Сридхарана до сих пор не мешают людям спрашивать и отвечать. - person biocyberman; 31.03.2017
comment
Отличный ответ, потому что он на самом деле сообщает вам что такое size_t, по крайней мере, в популярном дистрибутиве Linux. - person Andrey Portnoy; 11.01.2019

На странице руководства для types.h говорится:

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

person codaddict    schedule 31.03.2010

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

person supercat    schedule 05.10.2016
comment
У меня всегда был вопрос: если бы sizeof никогда не существовало, была бы необходимость в size_t? - person Dean P; 01.05.2019
comment
@DeanP: Возможно, нет, хотя тогда возникнет вопрос, какой тип аргумента следует использовать для таких вещей, как malloc(). Лично мне хотелось бы видеть версии, которые принимают аргументы типа int, long и long long, при этом некоторые реализации продвигают более короткие типы, а другие реализуют, например, lmalloc(long n) {return (n < 0 || n > 32767) ? 0 : imalloc(n);} [на некоторых платформах вызов imalloc(123) был бы дешевле, чем вызов lmalloc(123);, и даже на платформе, где size_t составляет 16 бит, код, который хочет выделить размер, вычисленный в значении `long` ... - person supercat; 01.05.2019
comment
... должен иметь возможность полагаться на сбой выделения, если значение больше, чем может обработать распределитель. - person supercat; 01.05.2019

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

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

size_t или ptrdiff_t не обязательно то же самое, что intptr_t или uintptr_t. Они различались на некоторых архитектурах, которые все еще использовались, когда size_t и ptrdiff_t были добавлены в Стандарт в конце 1980-х, и устарели, когда 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 на 64-разрядную версию, то он должен был быть конкретным в отношении того, какие свойства должен иметь тип, а не использовать одно, которое означало разные вещи в разных программах.

person Davislor    schedule 17.03.2019
comment
Несогласие с size_t и ptrdiff_t гарантированно будет иметь ширину 64 бита в 64-битной реализации и т. Д. гарантия завышена. Диапазон size_t в первую очередь определяется объемом памяти реализации. n-битная реализация - это, прежде всего, собственная ширина процессора целых чисел. Конечно, многие реализации используют память одинакового размера и ширину шины процессора, но существуют широкие собственные целые числа со скудной памятью или узкие процессоры с большим объемом памяти, которые разделяют эти два свойства реализации. - person chux - Reinstate Monica; 24.01.2020

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

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

Как правило, я предлагаю использовать int для большинства общих случаев и использовать _9 _ / _ 10_ только тогда, когда в этом есть особая необходимость (например, с mmap()).

person dtoux    schedule 30.10.2013

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

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

const size_t number;

size_t регулярно используется для индексации массивов и подсчета циклов. Если компилятор 32-bit, он будет работать с unsigned int. Если компилятор 64-bit, он также будет работать с unsigned long long int. Максимальный размер size_t в зависимости от типа компилятора.

size_t уже определен в <stdio.h> заголовочном файле, но он также может быть определен заголовками <stddef.h>, <stdlib.h>, <string.h>, <time.h> и <wchar.h>.

Пример (с const)

#include <stdio.h>

int main()
{
    const size_t value = 200;
    size_t i;
    int arr[value];

    for (i = 0 ; i < value ; ++i)
    {
        arr[i] = i;
    }

    size_t size = sizeof(arr);
    printf("size = %zu\n", size);
}

Вывод: size = 800


Пример (без const)

#include <stdio.h>

int main()
{
    size_t value = 200;
    size_t i;
    int arr[value];

    for (i = 0; i < value; ++i)
    {
        arr[i] = i;
    }

    size_t size = sizeof(arr);
    printf("size = %zu\n", size);
}

Вывод: size = 800

person Kalana    schedule 03.01.2020

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

person Mark    schedule 09.11.2013
comment
Я не могу принять вашу аргументацию. Вы говорите, что лучше, чтобы ошибка переполнения молча приводила к доступу к действительным данным в вашем массиве? - person maf-soft; 10.05.2016
comment
@ maf-soft правильно. если ошибка остается незамеченной, это хуже, чем сбой программы. почему этот ответ получил положительные голоса? - person yoyo_fun; 16.03.2017
comment
Если он обращается к действительным данным в вашем массиве, то это не ошибка, потому что беззнаковый тип не будет переполняться при предельном подписанном типе. Что это за логика, ребята? Скажем, по какой-то причине вы используете char для итерации по массиву из 256 элементов ... signed будет переполнен на 127, а 128-й элемент будет sigsegv, но если вы используете unsigned, он пройдет через весь массив, как задумано. Опять же, когда вы используете int, ваши массивы на самом деле не будут больше 2 миллиардов элементов, так что в любом случае это не имеет значения ... - person Purple Ice; 28.10.2018
comment
Я не могу представить себе ситуацию, в которой целочисленное переполнение не является ошибкой, независимо от того, является ли оно положительным или отрицательным. То, что вы не получаете segfault, не означает, что вы видите правильное поведение! И вы можете испытать ошибку сегментации или нет, независимо от того, положительное или отрицательное у вас смещение; все зависит от вашей схемы памяти. @PurpleIce, я не думаю, что вы говорите то же самое, что и этот ответ; ваш аргумент выглядит так, что вы должны выбрать тип данных, достаточно большой, чтобы вместить наибольшее значение, которое вы хотите поместить в него, что является просто здравым смыслом. - person Soren Bjornstad; 01.04.2019
comment
Тем не менее, я предпочитаю использовать беззнаковый тип для индексов цикла семантически; если ваша переменная никогда не будет отрицательной, вы также можете указать это в выбранном вами типе. Это также может позволить компилятору обнаружить ошибку, в которой значение оказалось отрицательным, хотя GCC, по крайней мере, довольно ужасен при обнаружении этой конкретной ошибки (однажды я инициализировал unsigned равным -1 и не получил предупреждения). Точно так же size_t семантически подходит для индексов массива. - person Soren Bjornstad; 01.04.2019

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

person Prince    schedule 12.08.2017

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
person bishwas pokharel    schedule 15.05.2018

Это зависит от платформы typedef. Например, на конкретной машине это может быть unsigned int или unsigned long. Вы должны использовать это определение для большей переносимости вашего кода.

person Artem    schedule 02.02.2021

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

So:

sizeof(size_t) >= sizeof(void*)
person David Zechiel    schedule 07.03.2013
comment
Не правда. Размер указателя может быть больше size_t. Несколько примеров: компиляторы C в реальном режиме x86 могут иметь 32-битные FAR или HUGE указатели, но size_t по-прежнему составляет 16 бит. Другой пример: Watcom C использовал специальный жирный указатель для расширенной памяти шириной 48 бит, а size_t - нет. На встроенном контроллере с архитектурой Гарварда у вас тоже нет корреляции, потому что оба относятся к разным адресным пространствам. - person Patrick Schlüter; 26.07.2013
comment
И на этом stackoverflow.com/ questions / 1572099 / есть еще примеры AS / 400 со 128-битными указателями и 32-битными size_t - person Patrick Schlüter; 26.07.2013
comment
Это вопиющая ложь. Однако оставим это здесь - person Antti Haapala; 16.10.2017