Целочисленное ли целое число используется как тип данных?

9

Используют ли большинство разработчиков приложений целые числа со знаком в тех местах, где они действительно хотят использовать целые числа без знака? Я делаю это все время, как и мои коллеги. Я не видел много других обширных кодовых баз (кроме Delphi VCL), и примеры в Интернете обычно используют целочисленные значения. Принимая во внимание, что разработчики VCL используют свои собственные типы данных (что было бы наиболее ленивым способом объявления переменных).

Что-то кажется немного ужасным в таком коде

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

когда это может быть написано как

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

Функционально эти записи почти всегда работают одинаково (и, надеюсь, продолжат работать одинаково даже в 64-битной Delphi). Но очень большие числа будут иметь проблемы с конверсией.

Но есть и недостатки использования неподписанных целых. Главным образом проистекает из того, как раздражает смешивать два.

На самом деле вопрос в том, действительно ли об этом думают или включают в лучшие практики? Обычно это зависит только от разработчика?

Питер Тернер
источник
5
Питер, ты ищешь только специфичные для Delphi ответы?
Адам Лир
3
@Anna Понимание того, как работают типы данных Delphi, поможет получить превосходный ответ. Я вполне уверен, что программисты на Си могли понять и ответить на этот вопрос.
Питер Тернер

Ответы:

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: не в Дельфи. (По крайней мере, если вы не сделаете что-то глупое, например, отключите встроенную проверку переполнения.) В результате вы получите исключение при переполнении счетчика вместо бесконечного цикла. Это все еще ошибка, но ее намного легче отследить.
Мейсон Уилер
Если ты так говоришь. Я всегда отключал проверку переполнения в Delphi; после бесконечной бомбардировки ложными срабатываниями от таких вещей, как хэш-коды и контрольные суммы, я просто полностью отказался от этой «функции». Но я полагаю, что вы правы, он бы поймал эту конкретную ошибку.
Ааронаут
@Aaronaught: Ну, да, вы бы хотели отключить его для таких вещей, как хэш-коды и контрольные суммы, которые специально предназначены для переполнения и переноса. Но для вычислений общего назначения, которые не рассчитаны на переполнение и наматывание, это важная функция безопасности, и отключение - это все равно, что ехать без ремня безопасности.
Мейсон Уилер
Возможно, вы забыли, но директивы проверки переполнения и компилятора были невероятно ошибочными в старых версиях Delphi. Я хорошо помню, как несколько раз рвал на себе волосы после того, как увидел, что отладчик остановился прямо посередине блока {$ O -} / {$ O +}, чтобы весело сообщить о переполнении. Через некоторое время я больше не мог принимать это и просто отключил это глобально. Опять же, да, это бы уловило эту проблему, но я все еще не думаю, что это стоит того количества ложных срабатываний. Каждому свое, конечно!
Ааронаут
3

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

  • Perspective. Размер карты тайлов ограничен 192x192 тайлами, поэтому я могу использовать байт для адресации тайлов и циклов. Но если размер карты будет увеличен, мне придётся проходить каждое использование и заменять его, например, словом. Когда мне нужно разрешить объекты вне карты, мне нужно будет снова перейти на smallint.

  • Loops. Часто я пишу цикл «от i: = 0 до Count-1», что происходит, если «i» - байт, а Count = 0 - цикл выполняется от 0 до 255. Не то, чтобы я этого хотел.

  • Унифицируйся. Проще запомнить и применить «var i: integer;» чем останавливаться в каждом случае и думать: «Хм .. здесь мы имеем дело с диапазоном 0..120 .. байт .. нет, подождите, нам может понадобиться -1 для неинициализированной .. краткости .. подождите .. что если 128 не достаточно .. Arrgh! " или "Почему в этом месте smallint, а не shortint?"

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

  • -1. Даже когда значения находятся в диапазоне 0..n-1, мне часто нужно устанавливать значение «нет значения / неизвестно / неинициализировано / пусто», что по общепринятой практике -1.

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

PS Когда я использую другие типы?

  • Счетчики, они никогда не бывают отрицательными и доступны только для чтения за пределами своего класса.
  • Причины производительности / памяти заставляют использовать более короткие типы данных в определенных местах.
Kromster
источник
1

Рекомендуется использовать тип данных, который соответствует потребностям в используемых данных (ожидаемые данные).

Примеры C #: если бы мне нужно было поддерживать только от 0 до 255, я бы использовал байт.

Если мне нужно было поддержать 1000000 негатива и позитива, тогда 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 в целом) длинные и ulong станут 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. Как ники, статистика, int действительно просто и Int32.
Джон Рейнор
1
Одна вещь , чтобы отметить здесь, что не все типы совместимы с спецификацией Common Language . uintявляется одним из таких несовместимых типов, что означает, что его не следует использовать в общедоступном API, чтобы не нарушить возможность использовать этот API на языках .NET, отличных от того, на котором написана библиотека. Именно поэтому .NET Framework API сам использует intгде uintбы делал.
Адам Лир
1

Целочисленные типы без знака должны использоваться только для представления кардинальных чисел в языках, где они представляют кардинальные числа. Из-за того, что компьютеры, работающие на C, работали, целочисленные типы без знака вели себя как члены алгебраических колец mod-2 ^ n (означая, что переполненные вычисления предсказуемо «обертывают»), а язык указывает, что во многих случаях такие типы требуется вести себя как абстрактные алгебраические кольца, даже если такое поведение будет несовместимо с поведением кардинальных чисел или математических чисел.

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

Однако, учитывая относительное отсутствие типов кардинальных чисел, лучше всего просто использовать целые числа для представления математических и кардинальных чисел.

Supercat
источник