Чи використовується ціле число занадто багато як тип даних?


9

Чи використовують більшість розробників додатків підписані цілі числа в тих місцях, де вони дійсно мають на увазі використовувати цілі числа, які не підписуються? Я роблю це постійно, так це роблять і мої колеги. Я не бачив багато інших обширних баз кодів (крім Delphi VCL) і прикладів в Інтернеті зазвичай використовують цілі числа. Тоді як розробники VCL використовують свої власні типи даних (що було б самим не лінивим способом декларування змінних).

Щось здається трохи жахливим щодо цього коду

TStuffRec = record
   recordID : Integer;
   thingID : Integer;
   otherThingID : Integer;
end;

коли це можна було б записати як

TStuffRec = record
   recordID : Cardinal;
   thingID : Cardinal;
   otherThingID : Cardinal;
end;

Функціонально ці записи майже завжди працюють однаково (і, сподіваємось, вони будуть працювати однаково навіть у 64-розрядних Delphi). Але дуже велика кількість буде мати проблеми з конверсією.

Але є й недоліки використання непідписаних ints. В основному випливає з того, як дратує змішання обох.

Справжнє запитання: чи це річ, яка насправді замислюється або включається в найкращі практики? Зазвичай це залежить лише від розробника?


5
Пітер, ти шукаєш лише відповіді, що стосуються Delphi?
Адам Лір

3
@Anna Розуміння того, як працюють типи даних Delphi, допоможе отримати найкращу відповідь. Я абсолютно впевнений, що програмісти на C могли зрозуміти і відповісти на це питання.
Пітер Тернер

Відповіді:


9

Однією з причин, чому я не використовую цілі типи без підписів у Delphi, є те, що вони можуть створювати проблеми при змішуванні з підписаними цілими числами. Ось один раз мене вкусив:

for i := 0 to List.Count - 1 do
  //do something here

Я iоголосив цілим числом без підпису (зрештою, це індекс у списку, який починається з 0, він ніколи не повинен бути від'ємним, правда?), Але коли List.Countбуло 0, це не замикає цикл, як очікувалося, оскільки 0 - 1оцінює дійсно високе додатне число. На жаль!

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


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

@Aaronaught: Не в Delphi. (Принаймні, ні, якщо ви не зробите щось дурне, як відключення перевірки вбудованого переповнення.) Ви отримаєте виняток, коли лічильник переповнений замість нескінченного циклу. Це все-таки помилка, але відстежити це набагато простіше.
Мейсон Уілер

Якщо ти так кажеш. Я завжди відключав перевірку переповнення в Delphi; після нескінченного обстрілу фальшивими позитивами з таких речей, як хеш-коди та контрольні суми, я просто повністю відмовився від цієї "функції". Але я припускаю, що ти маєш рацію, це спричинило б конкретну помилку.
Aaronaught

@Aaronaught: Ну так, ви хочете вимкнути його для таких матеріалів, як хеш-коди та контрольні суми, спеціально розроблені для переповнення та загортання. Але для розрахунків загального призначення, які не призначені для переповнення та загортання, це важлива функція безпеки, і вимкнути її начебто як їзда без ременів безпеки.
Мейсон Уілер

Можливо, ви забули, але директиви щодо перевірки переповнення та компілятора були неймовірно помилковими у старих версіях Delphi. Я можу яскраво пам’ятати, що виривав волосся кілька разів, коли побачив, як відладчик зупиняється прямо посеред блоку {$ O -} / {$ O +}, щоб весело повідомити про перелив. Через деякий час я вже не міг його прийняти, а просто відключив її в усьому світі. Знову так, це зачепило б це питання, але я все ще не думаю, що це вартує кількості помилкових позитивних результатів. Кожному своє, звичайно!
Aaronaught

3

Якщо чесно, я, як правило, використовую Integers за звичкою. Я звик до того, що вони пропонують досить великі діапазони для більшості ситуацій і дозволяють негативні значення (наприклад, -1). Дійсно, багато разів, використовуючи байти / слово / короткий шрифт, було б доречніше. Тепер, думаючи про це, я можу зосередитись на таких плямах:

  • Перспектива. Розмір Tilemap обмежений плитками 192x192, тому я міг використовувати байт для адреси плиток і циклів. Але якщо розмір карти буде збільшений, мені доведеться пройти кожне використання та замінити його, наприклад, word. Коли мені потрібно дозволити об'єкти, які не містять карту, мені доведеться знову перейти на smallint.

  • Петлі. Часто я пишу цикл "від i: = 0 до Count-1", що станеться, якщо "i" є байтом, а Count = 0 - це цикл, що працює від 0 до 255. Не те, щоб я цього хотів.

  • Уніформування. Простіше запам'ятати та застосувати "var i: integer;" ніж зупинятися в кожному випадку і думати "Гм .. тут ми маємо справу з діапазоном 0..120 .. байт .. ні, зачекайте, нам може знадобитися -1 для неініціалізованого .. короткого .. .. почекайте .. що якщо 128 недостатньо .. Арр! " або "Чому в цьому місці маленькиймінет, а не короткий потік?"

  • Поєднання. Коли мені потрібно поєднувати два або більше класів разом, вони можуть використовувати різні типи даних для своїх цілей, використання більш широких типів дозволяє пропускати непотрібні перетворення.

  • -1. Навіть коли значення знаходяться в діапазоні 0..n-1, мені часто потрібно встановити значення "no value / unknown / uninitialized / empty", що є загальною практикою -1.

Використання Integers дозволяє пропустити всі ці проблеми, забути про оптимізацію низького рівня там, де вона не потрібна, вийти на більш високий рівень і зосередитись на більш реальних проблемах.

PS Коли я можу використовувати інші типи?

  • Лічильники, вони ніколи не є негативними і читаються лише поза класом.
  • Причини продуктивності / пам’яті змушують використовувати коротші типи даних у певних місцях.

1

Найкраща практика - використовувати тип даних, що відповідає потребам даних, що використовуються (очікувані дані).

C # приклади: Якщо мені потрібно було підтримувати лише 0 до 255, я б використовував байт.

Якщо мені потрібно було підтримати 1 000 000 негативних і позитивних, то int.

Більше 4,2 мільярда, тоді використовуйте довгий.

Вибравши правильний тип, програма буде використовувати оптимальний об'єм пам'яті, а також різні типи використовувати різні обсяги пам'яті.

Ось посилання C # int від MSDN.

int 
 -2,147,483,648 to 2,147,483,647
 Signed 32-bit integer

uint 
 0 to 4,294,967,295
 Unsigned 32-bit integer

long 
 -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
 Signed 64-bit integer

ulong 
 0 to 18,446,744,073,709,551,615
 Unsigned 64-bit integer

У C # (або загалом .net) довго і недовго перетворюється на 128 біт на 128-бітній машині? Тому що в Delphi Integerтип даних - 32 біт на 32-бітній машині, і, мабуть, буде 64 біт на 64-бітній машині.
Пітер Тернер

1
@ Peter Turner: Ні, у C # intце лише скорочення System.Int32, незалежно від того, на якій машині працює код.
nikie

@nikie, це просто подобається type int System.Int32чи щось до цього? Чи можна це легко змінити в майбутній версії рамки?
Пітер Тернер

@Peter Turner / nikie (sizeof (int) .ToString ()); ==> Повертає 4 (sizeof (Int64) .ToString ()); ==> Повертає 8 на моїй 64-бітної ОС Windows. Як nikie, статистика, інт справді справедливий і Int32.
Джон Рейнор

1
Одна річ , щоб відзначити тут, що не всі типи сумісні з специфікацією Common Language . uintє одним з таких невідповідних типів, що означає, що він не повинен використовуватися в відкрито відкритому API, щоб уникнути порушення можливості використання цього API на мовах .NET, відмінних від мови, в яку написана бібліотека. Ось чому саме .NET Framework API сам використовує те, intде uintробити.
Адам Лір

1

Непідписані цілі типи повинні використовуватися лише для представлення кардинальних чисел на мовах, де вони представляють кардинальні числа. Через те, як працювали комп’ютери, на яких працював C, непідписані цілі типи поводилися як члени алгебраїчних кілець mod-2 ^ n (тобто обчислення, які переповнювались, «передплітали» передбачувано), а мова вказує, що у багатьох випадках такі типи є потрібно поводитись як абстрактні алгебраїчні кільця, навіть якщо така поведінка була б невідповідна поведінці кардинальних чисел або математичних цілих чисел.

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

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

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