В каких случаях типы данных 'uint' и 'short' лучше подходят, чем стандартный int (32)?


24

Я понимаю различия в возможностях и ценностях, которые они могут представлять, но кажется, что люди всегда используют Int32независимо от того, уместно ли это. Никто, кажется, никогда не использует unsigned version ( uint), хотя большую часть времени он подходит лучше, поскольку описывает значение, которое не может быть отрицательным (возможно, для представления идентификатора записи базы данных). Кроме того, никто никогда не использует, short/Int16независимо от требуемой емкости значения.

Объективно, есть ли случаи, когда лучше использовать uintили, short/Int16если да, то какие они?


13
Популярность не всегда является жизнеспособным показателем для оценки решений по разработке программного обеспечения. Тот факт, что практика популярна, не означает, что она подходит для вашего конкретного приложения или даже является хорошей практикой.
Роберт Харви


1
Короткий ответ, я думаю, заключается в том, что программисты привыкли к подписанной семантике и склонны принимать ее даже при работе с неподписанными типами (и, таким образом, беззнаковой семантикой). Большинство людей считают, что дело в том, что программист ленив или необразован, но этот программист на самом деле может быть очень образованным и очень осторожным и хочет избежать мелких ловушек. Если хотите, посмотрите на soundsoftware.ac.uk/c-pitfall-unsigned и anteru.net/2010/05/17/736 .
Теодорос Чатзигианнакис

В числе без знака знак более nullчем положительный или отрицательный. Если вы думаете о нем как о чем-то, что никогда не может быть отрицательным или всегда положительным, вы будете удивлены (и часто злы) результатами, потому что это не сработает, особенно если сравнивать или вычитать из / из подписанных значений.
Адам Д. Руппе

1
По моему опыту, многие программисты, которые когда-либо программировали на языке Си, по-прежнему заботятся о байтах, все еще в наши дни, гигабайт памяти и дискового пространства.
user1451111

Ответы:


25

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

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

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

  • Если вы пытаетесь представить что-то, что никогда не будет разумно отрицательным ( public uint NumberOfPeople), используйте тип без знака.
  • Если вы пытаетесь представить что-то, что никогда не может быть разумно больше 255 ( public byte DamagedToothCount), используйте байт.
  • Если вы пытаетесь представить что-то, что может быть больше 255, но никогда не значительным числом тысяч , используйте short ( public short JimmyHoffasBankBalance).
  • Если вы пытаетесь представить что-то, что может быть сотнями тысяч, даже миллионами, но вряд ли когда-нибудь достигнет нескольких миллиардов, используйте int ( public int HoursSinceUnixEpoch).
  • Если вы точно знаете, что это число может иметь неограниченно большое значение, или вы думаете, что оно может иметь несколько миллиардов, но вы не уверены, сколько миллиардов, лучшая ставка - длинная. Если long не достаточно велик, у вас есть интересная проблема, и вам нужно начать поиск чисел произвольной точности ( public long MyReallyGreatAppsUserCountThisIsNotWishfulThinkingAtAll).

Это рассуждение можно использовать повсюду при выборе между типами со знаком, без знака и с различными размерами и т. Д., Просто подумайте о логической истинности данных, которые вы представляете в реальности.


11
+1, хотя я должен прояснить, что телефонные «номера» - это не цифры, а строки цифр и, возможно, форматирование. Вы, кажется, знаете об этом, но мы не хотим показывать плохой пример, не так ли? Кроме того, произвольное ограничение диапазона некоторого значения является недальновидным антипаттерном - intвезде, если только вы не уверены, что проблемный домен действительно ограничивает значение - ни один банк не захочет жестко ограничить счета до 33 тыс. Фунтов стерлингов (и подумать о забаве когда это переполняется ...!).
Амон

3
Цель новой жизни: значительный перерасход средств, недопустимый для целого типа моего банковского счета.
recursion.ninja

11
Есть веские причины не использовать неподписанные типы в определенных местах, например, когда арифметика смешана между подписанным и беззнаковым. См. Каковы лучшие практики относительно неподписанных целых? ,

19
Я не согласен с аргументацией здесь. Типы без знака часто являются ошибкой, потому что вычитание и сравнение являются неожиданными, если вы привыкли к целым числам (они работают согласованным образом, но это не всегда "положительно"). Я бы избегал их, если у вас нет особых причин использовать их. Кроме того, почему размер имеет значение для байта по сравнению с коротким по сравнению с INT? Вы часто даже не экономите место, так как структуры будут дополняют эти элементы или массивы до определенного выравнивания. Я бы использовал байт только в том случае, если размер действительно важен (маловероятно, особенно для кода C #, который я видел), или если вы специально хотите обернуть в 255 для чего-то.
Адам Д. Руппе

4
"выгода - это пространство памяти и время процессора" ... Я не вижу ни одного случая, когда крошечные типы действительно экономили бы время процессора. Целочисленные операции никогда не выполняются быстрее, чем на типах машинного размера , т. Е. Что касается процессора, то вы также можете использовать его long. Экономия памяти может, конечно, косвенно сэкономить время за счет повышения эффективности линий кэширования и т. Д., Но OTOH проблемы выравнивания с небольшими типами могут косвенно стоить времени.
осталось около

16

Конечно, есть случаи, когда лучше использовать uintили shortили Int16. Когда вы знаете, что ваш диапазон данных будет соответствовать ограничениям этого типа переменной, тогда можно использовать этот тип.

В средах с ограниченным объемом памяти или при работе с большим количеством объектов может иметь смысл использовать переменную наименьшего размера. Например, существует существенная разница в размере для массива из миллиона элементов ints и shorts.

Часто это не происходит в реальном коде по одной или нескольким из следующих причин:

  • Ограничения данных не были известны заранее
  • Была вероятность того, что ограничения данных не были твердыми или, как было известно, вероятно, будут изменены
  • Была надежда на повторное использование функции с более широким диапазоном данных
  • Разработчик не нашел время, чтобы продумать ограничения
  • Экономия памяти была незначительной, чтобы оправдать использование меньшего типа переменной

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


8

В C, в контекстах, не связанных с целочисленным продвижением , были определены значения без знака, которые ведут себя как члены «обертывающего» абстрактного алгебраического кольца (поэтому для любых X и Y XY даст уникальное значение, которое при добавлении к Y даст X ), в то время как целочисленные типы со знаком были определены как ведущие себя как целые числа, когда вычисления находились в определенном диапазоне, и позволяли делать что-либо вообще, когда вычисления выходили за рамки этого. Числовая семантика в C #, однако, совершенно другая. Когда внутри проверенного числового контекста оба типа со знаком и без знака ведут себя как целые числа, при условии, что вычисления остаются в диапазоне и выдают, OverflowExceptionкогда они не делают; в неконтролируемом контексте они оба ведут себя как алгебраические кольца.

Единственный раз, как правило, стоит использовать любой тип данных меньшего размера, чем Int32когда это необходимо для упаковки или распаковки вещей для компактного хранения или транспортировки. Если нужно хранить полмиллиарда положительных чисел, и все они будут в диапазоне от 0 до 100, использование одного байта вместо четырех сэкономит 1,5 гигабайта памяти. Это большая экономия. Однако, если фрагмент кода должен хранить в общей сложности пару сотен значений, создание каждого из них одним байтом, а не четырьмя, сэкономит около 600 байтов. Наверное, не стоит беспокоиться.

Что касается неподписанных типов, единственное время, когда они действительно полезны, - это при обмене информацией или при делении чисел на части. Если, например, нужно выполнить математику для 96-разрядных целых чисел, то, вероятно, будет гораздо проще выполнять вычисления для групп из трех беззнаковых 32-разрядных чисел, чем для групп целых чисел со знаком. В противном случае не так много ситуаций, когда диапазон 32- или 64-разрядного значения со знаком был бы неадекватным, но такого же размера беззнакового значения было бы достаточно.


4

Обычно плохая идея использовать неподписанные типы, потому что они переполняются неприятными способами.x = 5-6внезапно бомба замедленного действия в вашем коде. Между тем, преимущества беззнаковых типов сводятся к одной дополнительной доле точности, и если этот бит того стоит для вас, вы почти наверняка должны использовать вместо этого больший тип.

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


3
В C переполнение со знаком даже хуже, чем переполнение без знака (потому что это неопределенное поведение, в то время как unsigned указывается для переворачивания как одометр). OTOH, подписанный overflow / underflow гораздо менее распространен на практике, чем unsigned underflow.
Кевин

Да, но переполнение со знаком обычно более очевидно и предсказуемо.
Джек Эйдли

Я вообще согласен, но вы должны знать, что , например, современные компиляторы могут оптимизировать i+1>iв 1случае iподписания, наряду с целым рядом другого неприятного поведения. Неподписанное переполнение может вызвать ошибку в угловом случае. Переполнение со знаком может сделать вашу программу бессмысленной .
Кевин

@JackAidley Я совершенно уверен, что то, что вы говорите, не имеет смысла, так как 5-6 дает один и тот же битовый шаблон, независимо от того, без знака он или нет.
Инго

@Ingo: как часто вы смотрите на битовые паттерны? Что имеет значение значение битовой комбинации, а не то, какие биты включены или выключены.
Джек Эйдли

2

Соответствие CLS часто забывают и, возможно, касаются вашего вопроса, когда речь идет конкретно о типах .NET . Не все типы доступны для всех языков, построенных на .NET Framework.

Если вы пишете код для использования на языках, отличных от C #, и хотите, чтобы этот код гарантированно взаимодействовал с максимально возможным количеством языков .NET, вы должны ограничить использование вашего типа теми, которые совместимы с CLS.

Например, ранние версии VB.NET (7.0 и 7.1) не поддерживали целые числа без знака ( UInteger):

http://msdn.microsoft.com/en-us/library/aa903459(v=vs.71).aspx

Целые числа без знака не соответствуют CLS, поэтому их следует использовать с осторожностью, если вы не уверены, кто будет вашим потребителем библиотеки классов.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.