с использованием uint vs int [закрыто]

83

Какое-то время я заметил, что программисты на C # обычно везде используют int и редко прибегают к uint. Но я так и не нашел удовлетворительного ответа на вопрос, почему.

Если ваша цель - совместимость, uint не должен появляться в общедоступных API, потому что не все языки интерфейса командной строки поддерживают целые числа без знака. Но это не объясняет, почему int настолько распространен даже во внутренних классах. Я подозреваю, что это причина того, что uint экономно используется в BCL.

В C ++, если у вас есть целое число, для которого отрицательные значения не имеют смысла, вы выбираете целое число без знака.

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

Однако при смешивании типов int и unit потребуется дополнительная осторожность и приведение типов.

Следует ли использовать uint больше? Почему?

Eloff
источник
Просто у меня было дежавю: D, почти точно такой же вопрос задавали совсем недавно.
KroaX
Что касается проверки нижней границы (если вам нужно написать свою собственную), вы можете заменить ее if (i < 0 || i >= length)на if (unchecked((uint)i) >= length). Итоговый IL будет иметь на одну инструкцию (ветвление) меньше всего и даст примерно такую ​​же производительность (бесконечно быстрее). Лично мне он нравится просто потому, что он чешет меня от проверки нижних границ. Другие, вероятно, будут возражать против этого из-за «непроверенного», но я утверждаю, что это очень хорошая строка для изучения ее значения, потому что она проста и мгновенно ясно из контекста, какова цель = помогает читателю учиться.
AnorZaken 09
Забыл упомянуть, что вышеуказанное оптимально для 64-битных сборок, так как оно будет выполнять сравнение с 64-битными. Для 32-битных сборок if (unchecked((uint)i) >= unchecked((uint)length))дает лучшую производительность. Однако это выглядит очень запутанным, и 64-битное сравнение по-прежнему более производительно, чем стандартная проверка границ с двойным ветвлением на 32-битной сборке, поэтому я не могу рекомендовать это в любой разумной ситуации. (Я в основном упоминаю об этом, чтобы указать, что в противном случае используется 64-битное сравнение, что может быть полезно для некоторых.)
AnorZaken
Господа, я утверждаю, что ваше первичное мнение никуда не годится. Если есть объективный ответ, я бы хотел его услышать. Я полностью за то, чтобы изменить свою практику на пользу.
Джошуа

Ответы:

50

uintЯ подозреваю, что ваше наблюдение о том, почему не используется в BCL, является основной причиной.

UInt32 не соответствует требованиям CLS, что означает, что он полностью не подходит для использования в общедоступных API. Если вы собираетесь использовать uint в своем частном API, это будет означать преобразование в другие типы - и обычно проще и безопаснее просто сохранить тот же тип.

Я также подозреваю, что это не так часто встречается при разработке на C #, даже когда C # является единственным используемым языком, в первую очередь потому, что он не распространен в BCL. Разработчики в целом стараются (к счастью) имитировать стиль фреймворка, на котором они строят - в случае C # это означает попытку сделать ваши API, общедоступные и внутренние, максимально похожими на BCL .NET Framework. Это означало бы экономное использование uint.

Рид Копси
источник
1
stackoverflow.com/questions/2013116/… - это вопрос, который касается аналогичной темы
Стефан
77

intкороче набрать чем uint.

jjnguy
источник
2
Я подозреваю, что это довольно близко к истине. Зачем использовать, uintкогда в 99% случаев (по моему опыту)int будет достаточно?
Мэтью Джонс,
12
@Justin: "Волшебные числа" вроде -1 - вообще не лучшая идея. Переход на long также означает использование 2x памяти без всякой причины ... "unit" определенно ценен, если вам не нужно взаимодействовать с другими API.
Reed Copsey,
28
Мне никогда не комфортно использовать intдля индексации массива, потому что у меня никогда не будет отрицательного индекса. Кажется совершенно очевидным, что uintв этом случае следует использовать a .
Mark H
2
Не говоря уже о более читабельном. Если вы когда-нибудь передадите свой код / ​​алгоритмы, чтобы их мог прочитать кто-то менее опытный, чем вы, использование большого количества uintможет немного повредить им. intвполне приемлемо для использования во всех ситуациях, когда вы контролируете дальность действия.
drharris
3
@MarkH Полностью согласился, но при выполнении обратной итерации может быть полезно в виде: for (int i = arr.Length - 1; i >= 0; i--) { }. Выполнение этого с помощью uint приведет к исключению переполнения или, что еще хуже, к бесконечному циклу.
Aidiakapi
19

Обычно intхватит. Если вы можете удовлетворить все следующие условия, вы можете использовать uint:

  • Это не для общедоступного API (поскольку uintне соответствует требованиям CLS).
  • Вам не нужны отрицательные числа.
  • Вам (возможно) понадобится дополнительный диапазон.
  • Вы не используете это в сравнении с < 0, как никогда true.
  • Вы не используете это в сравнении с >= 0, как никогда false.

Последнее требование часто забывают и вносят ошибки:

static void Main(string[] args)
{
    if (args.Length == 0) return;
    uint last = (uint)(args.Length - 1);

    // This will eventually throw an IndexOutOfRangeException:
    for (uint i = last; i >= 0; i--)
    {
        Console.WriteLine(args[i]);
    }
}
Даниэль А.А. Пельсмакер
источник
13

1) Вредная привычка. Шутки в сторону. Даже в C / C ++.

Подумайте об общем forобразце:

for( int i=0; i<3; i++ )
    foo(i);

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

2) intвоспринимается как родной тип машины.

Лорнова
источник
4

Я предпочитаю , uintчтобы , intесли отрицательное число не является на самом деле в пределах допустимых значений. В частности, принимать intпараметр, но бросать, ArgumentExceptionесли число меньше нуля, просто глупо - используйтеuint !

Я согласен с тем, что uintон недостаточно используется, и призываю всех использовать его чаще.

JSB ձոգչ
источник
7
Очень опасно принимать только юинты и не проверять границы. Если кто-то передает отрицательное значение, CLR интерпретирует его как большое int, то есть для -1 вы получите uint.maxvalue. Это нежелательное поведение.
Henri
19
@Henri: C # не имеет неявного преобразования из int в uint, поэтому нет «Если кто-то передает отрицательное значение». Конечно, проверка верхнего предела по-прежнему уместна (но теперь вам нужна только одна проверка вместо двух).
Бен Фойгт
1

Я программирую на более низком уровне приложения, где int редко превышают 100, поэтому отрицательные значения не являются проблемой (например, для материала типа i <myname.length ()), это просто старая привычка C - и короче для ввода, как упоминалось выше. Однако в некоторых случаях при взаимодействии с оборудованием, где я имею дело с флагами событий от устройств, uint важен в случаях, когда флаг может использовать самый левый (самый высокий) бит.

Честно говоря, для 99,9% своей работы я мог легко использовать ushort, но int, знаете ли, звучит намного лучше, чем ushort.

ddm
источник
1

Я создал оболочку Direct3D 10 на C #, и мне нужно использовать uint, если я хочу создавать очень большие буферы вершин. Большие буферы на видеокарте не могут быть представлены подписанным int.

UINT очень полезен, и глупо говорить иначе. Если кто-то думает только потому, что им никогда не нужно было использовать uint, никто другой не будет, то вы ошибаетесь.

zezba9000
источник
Это хороший случай, когда вы можете воспользоваться преимуществами более широкого диапазона.
Лео Гурдиан
0

Я думаю, это просто лень. C # по сути является выбором для разработки на настольных компьютерах и других машинах с относительно большими ресурсами.

C и C ++, однако, имеют глубокие корни в старых системах и встроенных системах, где память невелика, поэтому программисты привыкли тщательно продумывать, какой тип данных использовать. Программисты на C # ленивы, и, поскольку ресурсов в целом достаточно, никто особо не оптимизирует использование памяти (в общем, не всегда, конечно). Событие, если байта будет достаточно, многие программисты C #, включая меня, просто используют int для простоты. Более того, многие функции API принимают целые числа, что предотвращает приведение типов.

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

Наконец, выбор целого числа более математически правильный. Беззнаковые целые числа не существуют в математике (только натуральные числа). А поскольку большинство программистов имеют математическое образование, использование целых чисел более естественно.

Анри
источник
Я бы не сказал, что это лень, хотя у лени есть свои достоинства. Более того, большую часть времени меня просто не волнует вещь int / uint, я трачу свои мозговые циклы на такое решение и просто следую int. Оборудование дешевое, программисты могут быть дорогими.
SWeko
Программисты ленивы. Это плохо. Раймонд сказал бы, что программисты ненавидят платить налоги!
lornova
Я был бы первым администратором, который сказал бы нам, программистам на C #, ленивым, но это не обязательно плохо.
ChaosPandion
@Lorenzo, в университете я написал статью, в которой утверждал, что ленивый программист - хороший программист. В основном речь шла об оптимизации времени программиста, а не машинного времени.
Eloff
1
Хм, большинство ошибок программистов, которые я когда-либо видел (или делал), вызваны ленью ...
lornova
0

Я думаю, что во многом причина в том, что, когда впервые появился C, большинство примеров использовалось intдля краткости. Мы радовались тому, integerчто нам не нужно писать, как мы это делали с Фортраном и Паскалем, и в те дни мы обычно использовали их для таких повседневных вещей, как индексы массивов и счетчики циклов. Целые числа без знака были особым случаем для больших чисел, которым требовался последний дополнительный бит. Я думаю, что это естественный прогресс, когда привычки C продолжились в C # и других новых языках, таких как Python.

Эд Пауэр
источник
0

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

Другие языки (например, C) рассматривают N-битные беззнаковые типы как группу, которая оборачивается по модулю 2 ^ N. Обратите внимание, что вычитание N из члена такой группы не представляет собой числовое вычитание, а скорее дает член группы, который, когда к нему добавляется N, дает оригинал. Возможно, определенные операции, включающие смесь значений со знаком и без знака, на самом деле не имеют смысла и, возможно, должны были быть запрещены, но даже код, который неаккуратен со своими спецификациями таких вещей, как числовые литералы, обычно будет работать, и был написан код, который смешивает знаковые и беззнаковые типы, и, несмотря на то, что они небрежны, действительно работает, так что спецификация не будет меняться в ближайшее время.

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

суперкар
источник
0

Я знаю, что это, вероятно, старая ветка, но я хотел дать некоторые пояснения.

Возьмем int8, в котором вы можете хранить от –128 до 127, и он использует 1 байт, который в сумме составляет 127 положительных чисел.
Когда вы используете int8, один из битов используется для отрицательных чисел -128.
Когда вы используете Uint8, вы присваиваете отрицательные числа положительным, так что это позволяет вам использовать 255 положительных чисел с тем же объемом памяти 1 байт.
Единственный недостаток - это то, что вы потеряли возможность использовать отрицательные значения.
Другая проблема заключается в том, что не все языки программирования и базы данных поддерживают это.
На мой взгляд, единственная причина, по которой вы могли бы использовать это, - это когда вам нужно быть эффективным в программировании, например, в играх, и вам нужно хранить большие неотрицательные числа. Вот почему не многие программы используют его.

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

Надеюсь, это кому-то поможет.

Питер де Врис
источник