за допомогою uint проти int [закрито]


83

Я деякий час спостерігав, що програмісти на C #, як правило, використовують int скрізь і рідко вдаються до uint. Але я ніколи не знайшов задовільної відповіді, чому.

Якщо ваша мета - взаємодія, uint не повинен з’являтися в загальнодоступних API, оскільки не всі мови CLI підтримують цілі числа без знака. Але це не пояснює, чому int так поширений, навіть у внутрішніх класах. Я підозрюю, що саме з цієї причини uint економно використовується в BCL.

У C ++, якщо у вас є ціле число, для якого від'ємні значення не мають сенсу, ви вибираєте ціле число без знака.

Це чітко означає, що від’ємні числа заборонені чи очікувані, і компілятор перевірить за вас. Я також підозрюю, що стосується індексів масивів, що JIT може легко скинути перевірку нижньої межі.

Однак при змішуванні типів int та unit потрібен додатковий догляд та зліпки.

Чи слід більше використовувати uint? Чому?



Щойно навчившись, я мав дежавю: D, майже точно таке ж питання було задане невдовзі.
KroaX

Що стосується перевірок нижчих меж (якщо вам потрібно написати власні), ви можете замінити if (i < 0 || i >= length)на if (unchecked((uint)i) >= length). Отриманий ІЛ матиме в цілому одну (гілку) інструкцію менше і даватиме приблизно однакову продуктивність (нескінченно швидше). Особисто я люблю це просто тому, що воно дряпає мій свербіж проти перевірки нижчих меж. Інші, швидше за все, будуть сперечатися проти цього через "неперевірений", але я стверджую, що це дуже хороша лінія для вивчення його значення, оскільки це просто і миттєво зрозуміло з контексту, що мета = допомагає читачеві вчитися.
AnorZaken

Забув згадати, що вищезазначене є оптимальним для 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, загальнодоступними та внутрішніми, максимально схожими на .NET Framework BCL. Це означало б економно використовувати uint.


1
stackoverflow.com/questions/2013116/… - це питання, яке стосується подібної теми
Stephan

77

intкоротше набору, ніж uint.


2
Я підозрюю, що це досить близько до істини. Навіщо використовувати, uintколи 99% часу (на мій досвід) intбуде достатньо?
Метью Джонс,

12
@Justin: "Чарівні цифри", як -1, взагалі не є гарною ідеєю. Перехід на long означає використання 2x пам'яті без причини, а також ... "unit", безумовно, цінний, за умови, що вам не потрібно взаємодіяти з іншими API.
Рід Копсі,

28
Я ніколи не почуваюся комфортно, використовуючи intдля індексації масиву, тому що у мене ніколи не буде негативного індексу. Здається сліпо очевидним, що uintв цьому випадку слід використовувати a .
Mark H

2
Не кажучи вже, читабельніший. Якщо ви коли-небудь передасте код / ​​алгоритми для читання комусь іншому, який може бути менш досвідченим, ніж ви, використання багатьох uintможе трохи повісити їх. intцілком прийнятно використовувати в усіх ситуаціях, коли ви контролюєте діапазон, який він буде приймати.
drharris

3
@MarkH Повністю погодився, але при виконанні зворотного ітерації може бути корисно в вигляді: for (int i = arr.Length - 1; i >= 0; i--) { }. Виконання цього за допомогою uint призведе до виключення переповнення або ще гіршого, до нескінченного циклу.
Айдіякапі,

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це використовується недостатньо, і закликаю всіх інших використовувати його більше.


7
Дуже небезпечно приймати лише uints і не перевіряти межі. Якщо хтось передає від'ємне значення, CLR буде інтерпретувати це як великий int, тобто для -1 ви отримуєте uint.maxvalue. Це не бажана поведінка.
Анрі

19
@Henri: C # не має неявного перетворення з int на uint, тому немає "Якщо хтось передає негативне значення". Звичайно, обмеження на верхню межу все ще є доцільним (але зараз вам потрібна лише одна перевірка замість двох).
Ben Voigt

1

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

Чесно кажучи, за 99,9% моєї роботи я міг би легко використовувати ushort, але це, знаєте, звучить набагато краще, ніж ushort.


1

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

UINT дуже корисний і дурно говорити інакше. Якщо хтось думає лише тому, що йому ніколи не потрібно було використовувати uint, ніхто інший цього не зробить, ви помиляєтесь.


Це хороший випадок, коли ви можете скористатися широким асортиментом.
Лео Гардіан,

0

Я думаю, що це просто лінь. C # за своєю суттю є вибором для розробки на настільних ПК та інших машинах із відносно великими ресурсами.

Однак C і C ++ мають глибоке коріння у старих системах та вбудованих системах, де пам'ять обмежена, тому програмісти використовуються для ретельного продумування типу даних. Програмісти на C # ледачі, і оскільки ресурсів загалом достатньо, ніхто насправді не оптимізує використання пам'яті (загалом, звичайно, не завжди). Подія, якщо байту було б достатньо, багато програмістів C #, включаючи мене, просто використовують int для простоти. Більше того, багато функцій API приймають ints, тому це запобігає трансляції.

Я погоджуюсь, що вибір правильного типу даних - це хороша практика, але я думаю, що головною мотивацією є лінь.

Нарешті, вибір цілого числа є математично правильнішим. Непідписані ints в математиці не існують (лише натуральні числа). І оскільки більшість програмістів мають математичний досвід, використання цілого числа є більш природним.


Я б не сказав, що це лінь, хоча лінь має свої переваги. Більше того, більшу частину часу, я просто не надто дбаю про річ int / uint, щоб витрачати мої мозкові цикли на таке рішення і просто йти з int. Апаратне забезпечення дешеве, програмісти можуть бути дорогими.
SWeko

Програмісти лінуються. Це погана річ. Раймонд сказав би, що програмісти ненавидять платити податки!
lornova

Я був би першим, хто повідомив, що ми, програмісти на C #, ледачі, але це не обов'язково погано.
ChaosPandion

@Lorenzo, я написав статтю в університеті, заявивши, що ледачий програміст - хороший програміст. В основному мова йшла про оптимізацію для програміста, а не машинного часу.
Елофф,

1
Хм, більшість приписних помилок програмістів, які я коли-небудь бачив (чи робив), спричинені лінощами ...
lornova

0

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


0

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

Інші мови (наприклад, C) розглядають N-бітові непідписані типи як групу, яка обертається навколо модуля 2 ^ N. Зауважте, що віднімання N від члена такої групи не означає числового віднімання, а навпаки, дає член групи, який при додаванні до нього N дасть оригінал. Можна стверджувати, що певні операції, що стосуються сумішей знакових і непідписаних значень, насправді не мають сенсу і, можливо, повинні були бути заборонені, але навіть код, який недбалий своїми специфікаціями таких речей, як числові літерали, зазвичай буде працювати, і написано код, який змішує підписані і непідписані типи і, незважаючи на недбалість, все ж працює, що специфікація не може змінюватися найближчим часом.

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


0

Я знаю, що це, мабуть, стара тема, але я хотів дати кілька пояснень.

Давайте візьмемо int8, який ви можете зберегти від –128 до 127, і він використовує 1 байт, що становить 127 позитивних чисел.
Коли ви використовуєте int8, один із бітів використовується для від'ємних чисел -128.
Коли ви використовуєте Uint8, ви даєте від’ємні числа позитивним, тому це дозволяє використовувати 255 позитивних чисел з однаковим об’ємом зберігання 1 байт.
Єдиний результат - це те, що ви втратили можливість використовувати негативні значення.
Ще однією проблемою цього є не всі мови програмування, і бази даних це підтримують.
На мою думку, єдиною причиною, по якій ви б це використовували, є те, що вам потрібно бути ефективним, як-от ігрове програмування, і вам потрібно зберігати великі невід’ємні числа. Ось чому не так багато програм використовують це.

Основна причина полягає в тому, що сховище не є проблемою, і ви не можете гнучко використовувати його з іншим програмним забезпеченням, плагінами, базою даних або Api. Також, наприклад, банку знадобляться від'ємні числа для зберігання грошей тощо.

Сподіваюся, це комусь допоможе.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.