Знаковые и беззнаковые целые числа для длины / количества

Что лучше для представления переменной длины или числа: знаковые или беззнаковые целые числа?

Мне кажется, что C ++ STL предпочитает unsigned (std::size_t, как в std :: vector :: size (), вместо C # BCL предпочитает подписанные целые числа (например, ICollection.Count.

Учитывая, что длина или счетчик являются неотрицательными целыми числами, моя интуиция выбрала бы беззнаковый; но я не понимаю, почему дизайнеры .NET выбрали целые числа со знаком.

Какой подход лучше? Каковы плюсы и минусы каждого из них?


person Community    schedule 06.04.2012    source источник
comment
вы должны проверить это: stackoverflow.com/questions/3935165/   -  person IndieProgrammer    schedule 06.04.2012
comment
Я подозреваю, что C # использует целые числа со знаком, потому что целые числа без знака несовместимы с CLS .   -  person Joe    schedule 06.04.2012
comment
@Joe Вы подозреваете правильно. Это главным образом потому, что Microsoft хотела распространить межъязыковое сотрудничество, ключевой архитектурный элемент их инициативы .NET, среду CLR. Введение нескольких разновидностей целых чисел просто было неприемлемо из-за проблем совместимости между языками, поэтому оно было объявлено несовместимым с CLS. Это там, вы получите довольно маленькие предупреждения, если решите их использовать и хотите быть CLS-совместимым, так сказать, ценным членом общества. : Đ   -  person    schedule 06.04.2012


Ответы (4)


C ++ использует беззнаковые значения, потому что им нужен полный диапазон. В 32-битной системе язык должен позволять иметь вектор размером 4 ГБ, а не только 2 ГБ. (ОС может не позволять использовать все 4 ГБ, но сам язык не хочет мешать вам)

В .NET целые числа без знака несовместимы с CLS. Вы можете использовать их (на некоторых языках .NET), но это ограничивает переносимость и совместимость. Таким образом, для библиотеки базовых классов используются только целые числа со знаком.

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

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

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

Так что действительно, это зависит от обстоятельств. C ++ использует unsigned, потому что ему нужен диапазон. .NET использует подписанный, потому что он должен работать с языками, у которых нет без знака.

В большинстве случаев работают оба, а иногда подписание может позволить вашему коду более надежно обнаруживать ошибки.

person jalf    schedule 06.04.2012
comment
Вы, конечно, имели в виду UINT_MAX. Вы можете сравнить свой беззнаковый индекс с размером массива или INT_MAX, чтобы определить переполнение. Больше или равно? Тревога! - person Alexey Frunze; 06.04.2012
comment
@ Alex yah, исправил. и конечно, но размер массива может быть неизвестен. Или это может быть не простая операция индексации, а какая-то другая функция, в которой any положительное число потенциально допустимо, а отрицательное - нет. Дело в том, что иногда есть преимущества у обоих - person jalf; 06.04.2012
comment
@jalf: Во-первых, спасибо за отличный ответ. Правильно ли я понимаю - из того, что вы написали, - что использование целых чисел singed безопаснее, чем использование unsigned? Если да, то, возможно, по этой причине BCL использует целые числа со знаком помимо ограничений CLS? - person ; 06.04.2012
comment
@ Mr_C64: это зависит от контекста. Я просто указал один сценарий, где это может быть безопаснее. Алекс показал другое, где это не имеет значения (и где проверка ошибок становится проще с помощью unsigned int). Вам придется подумать об этом самостоятельно, применительно к конкретной ситуации, когда вам это нужно. :) - person jalf; 06.04.2012
comment
Верно, однако использование unsigned для охвата всего возможного диапазона действительно имело смысл в 16-битных системах, а не в 32-битных. Как только вы приблизитесь к миллиардам элементов, вы можете использовать 64-битные типы. Отсутствие подписи в повседневном коде в основном привлекает ошибки, предупреждения, беспорядок и боль. - person Jem; 06.12.2012
comment
Диапазон значений без знака является второстепенной проблемой по сравнению с тем фактом, что почти все арифметические операции с числами без знака дают определенные результаты, за исключением деления на ноль, больших сдвигов и умножения чисел без знака, тип которых меньше int, но чей продукт не подходит int [например, на машине с 64-битной int, (uint32_t) 3037000500 * (uint32_t) 3037000500]. - person supercat; 04.03.2014
comment
ИМО это было отказом от использования неподписанных целых чисел для размеров контейнера. Должно было быть просто int. Я уверен, что у очень многих людей было так много ошибок, потому что они начали использовать size_t в коде, чтобы избежать предупреждений компилятора. - person Pavel P; 06.10.2019

Естественно использовать беззнаковые типы для подсчетов и размеров, если мы не находимся в каком-то контексте, где они могут быть отрицательными, но все же иметь смысл. Я предполагаю, что C ++ следует той же логике своего старшего брата C, в котором strlen() возвращает size_t, а malloc() принимает size_t.

Проблема C ++ (и C) с целыми числами со знаком и без знака заключается в том, что вы должны знать, как они преобразуются друг в друга, когда вы используете смесь двух типов. Некоторые рекомендуют использовать целые числа со знаком для всех целых чисел, чтобы избежать проблемы невежества и невнимательности программистов. Но я думаю, что программисты должны знать, как использовать свои торговые инструменты (языки программирования, компиляторы и т. Д.). Рано или поздно их укусит преобразование, если не в том, что они написали, то в том, что есть у кого-то другого. Это неизбежно.

Итак, знайте свои инструменты, выбирайте то, что имеет смысл в вашей ситуации.

person Alexey Frunze    schedule 06.04.2012

Здесь есть несколько аспектов:

1) Максимальные значения: обычно максимальное значение числа со знаком составляет 1/2 от соответствующего максимального значения без знака. Например, в C максимальное короткое значение со знаком составляет 32767, тогда как максимальное короткое значение без знака равно 65535 (поскольку 1/2 диапазона не требуется для чисел -ve). Поэтому, если ваши ожидаемые длины или количества будут большими, представление без знака имеет больше смысла.

2) Безопасность: вы можете просматривать сеть на предмет ошибок переполнения целых чисел, но представьте себе такой код, как:

if (length <= 100)
{
  // do something with file
}

... тогда, если 'length' является значением со знаком, вы рискуете, что 'length' будет числом -ve (хотя и злонамеренным, некоторым приведением и т. д.), а код не выполнит ожидаемого вами. Я видел это в предыдущем проекте, где последовательность увеличивалась для каждой транзакции, но когда используемое нами целое число со знаком достигло максимального значения int со знаком (2147483647), оно внезапно стало -ve после следующего приращения, и наш код не смог обработать Это.

Просто некоторые вещи, о которых стоит подумать, независимо от основного языка / API.

person Gary Robinson    schedule 06.04.2012
comment
Другой проблемой может быть код типа while (--size >= 0) ... . Когда size без знака, условие всегда истинно. - person ; 06.04.2012
comment
С другой стороны, while(size-- > 0) - надежная идиома (хотя в основном на C / C ++, а не на C #, так как так много внимания уделяется использованию подписанных типов повсюду, что использование неподписанных типов представляет больше проблем, чем того стоит, поскольку вам нужно существенно преобразовать все время). Подписанные типы не спасут плохой код, они просто скроют логические ошибки :) - person Thomas; 14.11.2014
comment
соответствующие - ›Хотя size_t и ssize_t имеют похожие имена, их определения в POSIX не соответствуют. ssize_t может быть той же разрядной ширины, что и size_t, но может быть шире. ssize_t Используется для подсчета байтов или индикации ошибки говорит ничего о size_t. - person chux - Reinstate Monica; 04.01.2020

Если вы не разрабатываете повторно используемую библиотеку (в терминах .NET, например, проект VB.NET использует вашу библиотеку классов C #), выберите то, что вам подходит. Конечно, если вы создаете какой-либо тип DLL, и возможно, что ваша библиотека может быть использована в проекте с другим языком (опять же, на ум приходит VB.NET), тогда вам нужно помнить о несовместимых типах (неподписанные ).

person Chris    schedule 06.04.2012