Беззнаковое int против size_t

Я заметил, что современный код C и C++, похоже, использует size_t вместо int / unsigned int практически везде - от параметров для строковых функций C до STL. Мне любопытно, почему это происходит и какие преимущества это приносит.

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
507
0
216 255
8
Перейти к ответу Данный вопрос помечен как решенный

Ответы 8

Ответ принят как подходящий

Тип size_t - это целочисленный тип без знака, который является результатом оператора sizeof (и оператора offsetof), поэтому он гарантированно будет достаточно большим, чтобы содержать размер самого большого объекта, который может обрабатывать ваша система (например, статический массив из 8 ГБ).

Тип size_t может быть больше, равен или меньше, чем unsigned int, и ваш компилятор может делать предположения об этом для оптимизации.

Вы можете найти более точную информацию в стандарте C99, раздел 7.17, черновик которого доступен в Интернете в формате pdf, или в стандарте C11, раздел 7.19, также доступном как pdf черновик.

Неа. Подумайте о x86-16 с большой (не огромной) моделью памяти: указатели далеко (32-разрядные), но отдельные объекты ограничены 64 КБ (поэтому size_t может быть 16-разрядным).

dan04 28.11.2010 06:46

«размер самого большого объекта» - не плохая формулировка, а абсолютно правильная. Размер объекта может быть гораздо более ограничен, чем адресное пространство.

gnasher729 13.04.2014 01:39

"ваш компилятор может сделать предположение об этом": я надеюсь, что компилятор знает точно знает диапазон значений, которые может представлять size_t! Если нет, то кто?

Marc van Leeuwen 15.06.2014 09:42

@Marc: Я думаю, дело было больше в том, что компилятор мог бы сделай что-нибудь с этим знанием.

user1084944 01.09.2015 01:02

Я просто хочу, чтобы этот набирающий популярность тип не требовал включения файла заголовка.

user2023370 11.11.2016 13:26

На самом деле компиляторы обычно лучше оптимизируют, когда нет использует size_t, потому что беззнаковые типы определены более строго и, следовательно (при определенных обстоятельствах), помимо прочего, должны проверять переполнение. По сравнению с другими беззнаковыми типами простые операции, такие как индексирование указателя / массива, могут быть быстрее, потому что компилятору может, например, не нужно сначала преобразовывать в 64-битные целые числа (если ваш процессор выполняет только арифметику указателей в 64-битных).

yyny 05.03.2019 15:24

Вдобавок ко всему, адресное пространство редко превышает 48 бит даже в 64-битном режиме, поэтому, если вам не требуется доступ к элементам, превышающим 2 * sizeof(T) ГБ, я настоятельно рекомендую хранить размеры и индексы вашего массива как int вместо size_t всякий раз, когда вы можете получить прочь с этим. Может сэкономить до 50% памяти и даже ускорить код.

yyny 05.03.2019 15:30

@YoYoYonnY Конечно, без подписи. Но не подписано. Есть много проблем с подписью, включая жалобы на линты. Конечно, это не означает, что можно легко взять старый код, использующий int, и без проблем изменить их все на беззнаковые. Я согласен с тем, что бывают случаи, когда int имеет свои достоинства (в том числе тот факт, что некоторые системные вызовы используют их как функции сокетов!), Но это не всегда легкое решение (хотя иногда это легко сделать).

Pryftan 18.11.2019 00:50

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

Например, в 64-битных системах int и unsigned int могут иметь ширину 32 бита, но size_t должен быть достаточно большим, чтобы хранить числа больше 4G.

«объект» - это язык, используемый стандартом.

R.. GitHub STOP HELPING ICE 07.08.2010 22:28

Я думаю, что size_t должен был бы быть таким большим, если бы компилятор мог принимать тип X, такой, что sizeof (X) давал бы значение больше 4G. Большинство компиляторов отклонят, например, typedef unsigned char foo[1000000000000LL][1000000000000LL] и даже foo[65536][65536]; могут быть законно отклонены, если они превышают задокументированный предел, определенный реализацией.

supercat 29.03.2014 00:24

@MattJoiner: Формулировка в порядке. «Объект» вовсе не расплывчатый, а скорее означает «область хранения».

Lightness Races in Orbit 05.04.2015 04:18

Классический C (ранний диалект C, описанный Брайаном Керниганом и Деннисом Ричи в The C Programming Language, Prentice-Hall, 1978) не обеспечивает size_t. Комитет по стандартам C представил size_t, чтобы устранить проблему переносимости

Подробно объяснено на embedded.com (с очень хорошим примером)

Еще одна отличная статья, объясняющая как size_t, так и ptrdiff_t: viva64.com/en/a/0050

Ihor Kaharlichenko 15.06.2011 17:58

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

При написании кода C вы должны всегда использовать size_t всякий раз, когда имеете дело с диапазонами памяти.

С другой стороны, тип int в основном определяется как размер (подписанного) целочисленного значения, которое хост-машина может использовать для наиболее эффективного выполнения целочисленной арифметики. Например, на многих старых компьютерах типа ПК значение sizeof (size_t) будет 4 (байта), а sizeof (int) будет 2 (байтом). 16-битная арифметика была быстрее 32-битной, хотя ЦП мог обрабатывать (логическую) память размером до 4 ГиБ.

Используйте тип int только в том случае, если вам важна эффективность, поскольку его фактическая точность сильно зависит как от параметров компилятора, так и от архитектуры машины. В частности, стандарт C определяет следующие инварианты: sizeof (char) <= sizeof (short) <= sizeof (int) <= sizeof (long), не накладывая никаких других ограничений на фактическое представление точности, доступной программисту для каждого из эти примитивные типы.

Примечание. Это НЕ то же самое, что и в Java (которая фактически определяет битовую точность для каждого из типов char, byte, short, int и long).

де-факто определение int состоит в том, что это 16 бит на 16 машинах и 32 бит на любых машинах большего размера. Было написано слишком много кода, который предполагает, что int имеет ширину 32 бита, чтобы изменить это сейчас, и в результате люди всегда должны использовать size_t или {, u} int {8,16,32,64} _t, если им нужно что-то конкретное - - в качестве меры предосторожности люди должны всегда использовать их вместо целочисленных целочисленных типов.

Clearer 07.12.2014 16:51

«Это целое число без знака, способное выражать в байтах размер любого диапазона памяти, поддерживаемого на хост-машине». -> Нет. size_t может представлять размер любого отдельного объекта (например, числа, массива, структуры). Весь диапазон памяти может превышать size_t

chux - Reinstate Monica 09.10.2015 04:36

«При написании кода C вы всегда должны использовать size_t, когда имеете дело с диапазонами памяти». - это означает, что каждый индекс для каждого массива должен быть size_t - я надеюсь, вы не это имеете в виду. В большинстве случаев мы не имеем дело с массивами, где количество элементов адресного пространства + даже имеет значение. В этих случаях вам следует взять size_t. В любом другом случае вы берете индексы из (подписанных) целых чисел. Потому что путаница (которая возникает без предупреждения), возникающая из-за непредвиденного поведения неполного заполнения неподписанных, более распространена и хуже, чем проблемы переносимости, которые могут возникнуть в других случаях.

johannes_lalala 10.02.2020 02:51

@johannes_lalala Надеюсь, вы не это имеете в виду. Нет другого типа, который гарантированно был бы достаточно большим, чтобы содержать наибольший допустимый индекс массива. Целые числа со знаком плохи, когда дело доходит до переполнения или потери значимости, поскольку они вызывают UB, в то время как беззнаковое переполнение и недостаточное заполнение не вызывают UB, но хорошо определены. size_t следует использовать для всех неотрицательных индексов массива.

12431234123412341234123 11.09.2020 15:28

Да, я сделал. Большие индексы почти не нужны, за 20 лет никогда. Если вы думаете, что можете достичь большого индекса, используйте size_t. Во всех остальных случаях не делайте этого. Рассмотрим эту общую проверку диапазона: index = x - y.. later: if (index < 0) -> fail, otherwise z = arr[index] .. Что произойдет, если вы используете здесь целые числа без знака. Это также официальная позиция комитета C++ по этой теме, кстати

johannes_lalala 11.09.2020 15:46

size_t - размер указателя.

Таким образом, в 32-битной модели size_t или в обычной модели ILP32 (integer, long, pointer) size_t составляет 32 бита. а в 64-битной или общей модели LP64 (длинный, указатель) size_t составляет 64 бита (целые числа по-прежнему являются 32-битными).

Есть и другие модели, но это те, которые использует g ++ (по крайней мере, по умолчанию)

size_t не обязательно того же размера, что и указатель, хотя обычно это так. Указатель должен указывать на любое место в памяти; size_t должен быть достаточно большим, чтобы представлять размер самого большого отдельного объекта.

Keith Thompson 13.04.2012 03:13

intptr_t, вероятно, имеет тот же размер, что и указатель void *. Это не является обязательным требованием, но intptr_t должен иметь возможность хранить все возможные допустимые значения для указателя void *. Но size_t не имеет этого требования. Также size_t это как минимум 16 бит, в то время как указатель может быть меньше.

12431234123412341234123 11.09.2020 15:22

Короче говоря, size_t никогда не бывает отрицательным и максимизирует производительность, потому что typedef должен быть целочисленным типом без знака, достаточно большим, но не слишком большим, чтобы представлять размер максимально возможного объекта на целевой платформе.

Размеры никогда не должны быть отрицательными, и действительно, size_t является беззнаковым типом. Кроме того, поскольку size_t является беззнаковым, вы можете хранить числа, которые примерно вдвое больше, чем в соответствующем знаковом типе, потому что мы можем использовать знаковый бит для представления величины, как и все другие биты в беззнаковом целом числе. Когда мы получаем еще один бит, мы умножаем диапазон чисел, который мы можем представить, примерно в два раза.

Итак, вы спросите, почему бы просто не использовать unsigned int? Возможно, он не сможет вместить достаточно большие числа. В реализации, где unsigned int составляет 32 бита, наибольшее число, которое он может представить, - 4294967295. Некоторые процессоры, например IP16L32, могут копировать объекты размером более 4294967295 байт.

Итак, вы спросите, почему бы не использовать unsigned long int? Это снижает производительность на некоторых платформах. Стандарт C требует, чтобы long занимал как минимум 32 бита. Платформа IP16L32 реализует каждое 32-битное слово как пару 16-битных слов. Почти все 32-битные операторы на этих платформах требуют двух инструкций, если не больше, потому что они работают с 32-битными двумя 16-битными порциями. Например, для перемещения 32-битной длины обычно требуются две машинные инструкции - по одной для перемещения каждого 16-битного фрагмента.

Использование size_t позволяет избежать этого снижения производительности. Согласно эта фантастическая статья, «Тип size_t - это typedef, который является псевдонимом для некоторого целочисленного типа без знака, обычно unsigned int или unsigned long, но, возможно, даже unsigned long long. Каждая реализация стандарта C должна выбирать достаточно большое целое число без знака, но не больше, чем необходимо. - представить размер максимально возможного объекта на целевой платформе ".

Извините, что комментирую это спустя столько времени, но мне просто нужно было подтвердить наибольшее число, которое может содержать unsigned int - возможно, я неправильно понимаю вашу терминологию, но я подумал, что наибольшее число, которое может содержать unsigned int, - 4294967295, 65356 - максимум беззнакового короткого замыкания.

Mitch 09.04.2012 15:05

Если ваш unsigned int занимает 32 бита, то да, самое большое число, которое он может содержать, равно 2 ^ 32-1, что составляет 4294967295 (0xffffffff). У вас есть еще вопрос?

Rose Perrone 11.04.2012 08:24

Никаких других вопросов, мне просто было любопытно, почему вы использовали 65,356, что подразумевает 16-битный беззнаковый int, который, как я никогда не знал, является наиболее распространенным случаем.

Mitch 11.04.2012 11:25

@Mitch: наибольшее значение, которое может быть представлено в unsigned int, может варьироваться и действительно варьируется от одной системы к другой. Это должен быть по меньшей мере65536, но обычно это 4294967295, а в некоторых системах может быть 18446744073709551615 (2 ** 64-1).

Keith Thompson 13.04.2012 03:12

Ох, хорошо. Есть ли стандарт или что-то, что требует, чтобы оно было не менее 65536? Кроме того, я только что понял, что пишу 65356 вместо 65536 - упс!

Mitch 14.04.2012 19:10

В этой статье говорится, что: Использование unsigned int в качестве типа параметра, например: void * memcpy (void * s1, void const * s2, unsigned int n); отлично работает на любой платформе, в которой целое число без знака может представлять размер самого большого объекта данных. Тогда мы можем сказать size_t = unsigned int. Можно ли сказать, что между ними нет разницы? (мой компьютер 32 бит)

oiyio 09.09.2012 12:41

Наибольшее значение, которое может содержать 16-битное целое без знака, - 65535, а не 65536. Небольшое, но важное отличие, поскольку 65536 совпадает с 0 в 16-битном беззнаковом int.

Sie Raybould 11.12.2013 03:00

«Стандарт или что-то в этом роде», который диктует, что беззнаковый int должен поддерживать как минимум 65 536 различных значений, - это стандарт C (стандарт C++ говорит то же самое).

gnasher729 13.04.2014 01:41

@ gnasher729: Вы уверены в стандарте C++? После некоторого поиска у меня сложилось впечатление, что они просто удалили все абсолютные гарантии относительно целочисленных диапазонов (за исключением unsigned char). Похоже, что стандарт нигде не содержит строки «65535» или «65536», а «+32767» встречается только (1,9: 9) в примечании как наибольшее целое число возможный, представимое в int; не дается никаких гарантий, даже если INT_MAX не может быть меньше этого!

Marc van Leeuwen 15.06.2014 11:38

@MarcvanLeeuwen в 18.3.3 / 2, стандарт C++ 11 говорит о <climits>: «Содержимое такое же, как заголовок стандартной библиотеки C <limits.h>». Полагаю, что требования к содержанию такие же. C99 говорит в 5.2.4.2.1 / 1: «Их определяемые реализацией значения должны быть равны или больше по величине (абсолютному значению), чем показанные, с тем же знаком.», За которыми следуют сами значения.

Ruslan 17.04.2016 12:59

@oiyio Не обязательно. Проверьте значение SIZE_MAX и UINT_MAX, а также ULONG_MAX (см. Заголовочные файлы stdint.h и limits.h). Конечно, это для C - я действительно плохо отреагировал на C++, но даже в этом случае есть ограничения.

Pryftan 18.11.2019 00:53

"но не слишком большой" Я не согласен, size_t должен быть как минимум 16-битным, но на некоторых платформах самый большой объект может иметь размер только 256 байт. Некоторые платформы не имеют даже 256 байт ОЗУ. Там будет 9-битный (и, вероятно, 8-битный, но тогда размер самого большого объекта всего 255 байт) будет достаточно.

12431234123412341234123 11.09.2020 15:18

Этот отрывок из руководства по glibc 0.02 также может быть уместен при исследовании темы:

Существует потенциальная проблема с типом size_t и версиями GCC до выпуска 2.4. ANSI C требует, чтобы size_t всегда был беззнаковым. Для совместимости с заголовочными файлами существующих систем GCC определяет size_t в stddef.h' to be whatever type the system'ssys / types.h 'определяет его как. Большинство систем Unix, которые определяют size_t в sys / types.h, определяют его как знаковый тип. Некоторый код в библиотеке зависит от того, является ли size_t беззнаковым типом, и не будет работать правильно, если он подписан.

Код библиотеки GNU C, который ожидает, что size_t будет беззнаковым, верен. Определение size_t как знакового типа неверно. Мы планируем, что в версии 2.4 GCC всегда будет определять size_t как беззнаковый тип, а fixincludes' script will massage the system'ssys / types.h ', чтобы не конфликтовать с этим.

А пока мы работаем над этой проблемой, явно сообщая GCC использовать беззнаковый тип для size_t при компиляции библиотеки GNU C. `configure 'автоматически определит, какой тип GCC использует для упорядочивания size_t, чтобы при необходимости отменить его.

Если мой компилятор установлен на 32-битный, size_t не что иное, как typedef для unsigned int. Если мой компилятор установлен на 64-битный, size_t не что иное, как typedef для unsigned long long.

Может быть просто определен как unsigned long для обоих случаев в некоторых ОС.

StaceyGirl 17.08.2018 23:50

Другие вопросы по теме